using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.IO;
using System.Xml.Serialization;
using System.Diagnostics;
using Waymex.Diagnostics;
using Waymex.Buffers;

namespace Waymex.Gps.Garmin
{
    /// <summary>
    /// Summary description for GPSUSBTransport.
    /// </summary>
    internal class UsbTransport : System.IDisposable
    {
        // error constants
        private const string ERR_9000_MSG = "GPS Device not detected on Universal Serial Bus.";
        private const string ERR_9001_MSG = "Unable to obtain handle to USB Device.";
        private const string ERR_9002_MSG = "Unable to read USB Interrupt In.";
        private const string ERR_9003_MSG = "Invalid Device ID used to Read Data.";
        private const string ERR_9004_MSG = "Unable to start a session with the USB Device.";
        private const string ERR_9005_MSG = "Unable to find USB Device Driver.";
        private const string ERR_9006_MSG = "Unable to get the name of the Device.";
        private const string ERR_9007_MSG = "Packet Start not found in buffer.";
        private const string ERR_9008_MSG = "Timed out during a Write Operation.";
        private const string ERR_9009_MSG = "Timed out during a Read Operation.";
        private const string ERR_3010_MSG = "The argument must be greater than zero.";
        private const string ERR_3011_MSG = "The argument length must be greater than zero.";
        private const string ERR_3012_MSG = "CreateFile returned the API Error: ";
        private const string ERR_3013_MSG = "CloseHandle returned the API Error: ";

        //usb guid
        private const string GARMIN_GUID = "2C9C45C2-8E7D-4C08-A12D-816BBAE722C0";

        // file constants
        private const UInt32 FILE_ANY_ACCESS = 0;
        private const UInt32 FILE_DEVICE_UNKNOWN = 0x22;
        private const UInt32 FILE_READ_ACCESS = 0x1;
        private const UInt32 FILE_WRITE_ACCESS = 0x2;
        private const UInt32 FILE_SHARE_READ = 0x1;
        private const UInt32 FILE_SHARE_WRITE = 0x2;
        private const UInt32 FILE_SHARE_DELETE = 0x4;
        private const UInt32 FILE_FLAG_OVERLAPPED = 0x40000000;
        private const UInt32 FILE_ATTRIBUTE_NORMAL = 0x80;
        private const UInt32 GENERIC_WRITE = 0x40000000;
        private const UInt32 GENERIC_READ = 0x80000000;
        private const UInt32 CREATE_ALWAYS = 0x2;
        private const UInt32 OPEN_EXISTING = 0x3;
        private const UInt32 OPEN_ALWAYS = 0x4;

        private const UInt32 FORMAT_MESSAGE_FROM_SYSTEM = 0x1000;
        private const Int32 INVALID_HANDLE_VALUE = -1;

        private const Int32 METHOD_BUFFERED = 0;
        private const Int32 METHOD_IN_DIRECT = 1;
        private const Int32 METHOD_OUT_DIRECT = 2;
        private const Int32 METHOD_NEITHER = 3;


        private const Int32 INTERRUPT_IN_BUFFER_SIZE = 8000000;
        private const Int32 BULK_IN_BUFFER_SIZE = 8000000;
        private const Int32 STANDARD_PACKET_SIZE = 12;

        private const string OVERLAPPED_EVENT_NAME_DEVICEIOCTRL = "USBGPSDeviceIOCtrlEvent";
        private const string OVERLAPPED_EVENT_NAME_READFILE = "USBGPSReadFileEvent";
        private const string OVERLAPPED_EVENT_NAME_WRITEFILE = "USBGPSWriteFileEvent";

        private const int IO_TIMEOUT = 1;  //timeout waiting for the GetOverlappedResult api
        private const int BOUT_TIMEOUT = 8;  //timeout waiting for the GetOverlappedResult api
        private const int BIN_TIMEOUT = 1;  //timeout waiting for the GetOverlappedResult api
        private const int OPEN_USB_TIMEOUT = 2;  //timeout waiting for create file to complete

        private const int GARMIN_USB_API_VERSION = 1;
        private const int GARMIN_USB_MAX_BUFFER_SIZE = 4096;
        private const int GARMIN_USB_INTERRUPT_DATA_SIZE = 64;

        //usb packet type constants (can't use enum as type must be byte
        private const byte PACKET_TYPE_TRANSPORT = 0;
        private const byte PACKET_TYPE_APPLICATION = 20;
        private const byte BULK_DATA_AVAILABLE_PACKET_ID = 2;
        private const byte START_SESSION_PACKET_ID = 5;
        private const byte SESSION_STARTED_PACKET_ID = 6;
        private const float READ_INT_BUFFER_TIMEOUT = 1;   //seconds waiting for packet ariving in buffer
        private const float READ_BI_BUFFER_TIMEOUT = 1;   //seconds waiting for packet ariving in buffer

        private const int API_TRUE = 1;
        private const int API_FALSE = 0;


        //Misc Constants
        private const int NOSUPPORT = -1;
        private const string LOG_LINE = "------------------------------------------------------------------";
        private const int TIMER_INTERVAL = 10;

        //Member Variables
        // member variables
        // the following are vars not constants as they are populated by the GetCtlCode function
        private uint IOCTL_GARMIN_USB_API_VERSION = 0;
        private uint IOCTL_GARMIN_USB_API_INTERRUPT_IN = 0;
        private uint IOCTL_GARMIN_USB_BULK_OUT_PACKET_SIZE_CTL_CODE = 0;

        private LinkProtocol miProtocol;								//protocol to be used
        private int[] mPIDData = new int[27];//PID Data table Data
        private bool mblnReadBulkIn = false; //gets set when Interrupt in says read bulkin

        //new buffer class
        ByteFifo m_bufferINT = new ByteFifo(INTERRUPT_IN_BUFFER_SIZE);
        ByteFifo m_bufferBI = new ByteFifo(BULK_IN_BUFFER_SIZE);


        ////create bulk in buffer (base 0)
        //private byte[] mbytBIBuffer = new byte[BULK_IN_BUFFER_SIZE];
        //private long mlngBIBufferInPtr = 0; //points at next free buffer byte
        //private long mlngBIBufferOutPtr = 0; //points at first byte of buffer

        ////create interrupt in buffer (base 0)
        //private byte[] mbytINTBuffer = new byte[INTERRUPT_IN_BUFFER_SIZE];
        //private long mlngINTBufferInPtr = 0; //points at next free buffer byte
        //private long mlngINTBufferOutPtr = 0;//points at first byte of buffer

        //threading stuff
        private Thread InterruptInThread = null;
        private Thread BulkInThread = null;
        private ReaderWriterLock objReadWriteLock = new ReaderWriterLock();


        //		internal enum LinkProtocol
        //		{
        //			/// <summary>0</summary>
        //			L000 = 0,
        //			/// <summary>1</summary>
        //			L001 = 1,
        //			/// <summary>2</summary>
        //			L002 = 2
        //		}

        internal enum usbPacketType //int
        {
            usbPacketTypeTransport = 0,
            usbPacketTypeApplication = 20,
            usbPacketTypeUnknown = 255,
        }
        internal enum usbPacketSource //int
        {
            usbPacketSourceUnknown = 0,
            usbPacketSourceInterruptIn = 1,
            usbPacketSourceBulkIn = 2,
            usbPacketSourceBulkOut = 3,
        }

        private class clsUsbPacket
        {
            internal usbPacketType PacketType = usbPacketType.usbPacketTypeUnknown;
            internal byte[] Reserved_A = new byte[3];
            internal short PacketID = 0;
            internal byte[] Reserved_B = new byte[2];
            internal int DataSize = 0;
            internal byte[] Data = null;
            internal usbPacketSource PacketSource = usbPacketSource.usbPacketSourceUnknown;
        }
        /// <summary>
        /// Thread safe class 
        /// </summary>
        private class DeviceHandles
        {
            private IntPtr m_hDevice;
            private IntPtr m_hDevIoEvent;
            private IntPtr m_hBulkReadEvent;
            private IntPtr m_hBulkWriteEvent;
            private bool m_overlapped;

            private object syncLock = new object();

            internal IntPtr Device
            {
                get
                {
                    lock (syncLock)
                    {
                        return m_hDevice;
                    }
                }
                set
                {
                    lock (syncLock)
                    {
                        m_hDevice = value;
                    }
                }
            }
            internal IntPtr DevIoEvent
            {
                get
                {
                    lock (syncLock)
                    {
                        return m_hDevIoEvent;
                    }
                }
                set
                {
                    lock (syncLock)
                    {
                        m_hDevIoEvent = value;
                    }
                }
            }
            internal IntPtr BulkReadEvent
            {
                get
                {
                    lock (syncLock)
                    {
                        return m_hBulkReadEvent;
                    }
                }
                set
                {
                    lock (syncLock)
                    {
                        m_hBulkReadEvent = value;
                    }
                }
            }
            internal IntPtr BulkWriteEvent
            {
                get
                {
                    lock (syncLock)
                    {
                        return m_hBulkWriteEvent;
                    }
                }
                set
                {
                    lock (syncLock)
                    {
                        m_hBulkWriteEvent = value;
                    }
                }
            }
            internal bool Overlapped
            {
                get
                {
                    lock (syncLock)
                    {
                        return m_overlapped;
                    }
                }
                set
                {
                    lock (syncLock)
                    {
                        m_overlapped = value;
                    }
                }
            }
        }

        //[StructLayout(LayoutKind.Sequential)]
        //private struct DEVICE_HANDLES
        //{
        //    internal IntPtr hDevice;
        //    internal IntPtr hDevIoEvent;
        //    internal IntPtr hBulkReadEvent;
        //    internal IntPtr hBulkWriteEvent;
        //    internal bool blnOverlapped;
        //}

        //private DEVICE_HANDLES mutDeviceHandles;			//stored device handles returned from OpenUSB function
        private DeviceHandles deviceHandles;			    //stored device handles returned from OpenUSB function

        internal UsbTransport()
        {
            IOCTL_GARMIN_USB_API_VERSION = (uint)GetCtlCode(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS);
            IOCTL_GARMIN_USB_API_INTERRUPT_IN = (uint)GetCtlCode(FILE_DEVICE_UNKNOWN, 0x850, METHOD_BUFFERED, FILE_ANY_ACCESS);
            IOCTL_GARMIN_USB_BULK_OUT_PACKET_SIZE_CTL_CODE = (uint)GetCtlCode(FILE_DEVICE_UNKNOWN, 0x851, METHOD_BUFFERED, FILE_ANY_ACCESS);

            SetPIDValues(LinkProtocol.L001);
            deviceHandles = new DeviceHandles();

        }
        public virtual void Dispose()
        {
            Dispose(false);
        }
        /// <summary>
        /// Releases all resources and destroys the object.
        /// </summary>
        protected virtual void Dispose(bool disposing)
        {
            //Check to see if Dispose has already been called.
            //and unmanaged resources.
            if (disposing)
            {
                //Dispose managed resources.
                CloseUsbDevice();
            }
            //Release unmanaged resources. If disposing is false,
            //only the following code is executed.      

            //Note that this is not thread safe.
            //Another thread could start disposing the object
            //after the managed resources are disposed,
            //but before the disposed flag is set to true.
        }

        internal long GetCtlCode(long xi_lngDeviceType, long xi_lngFunction,
            long xi_lngMethod, long xi_lngAccess)
        {
            return (xi_lngDeviceType * Convert.ToInt64((RaiseToPower(2, 16)))) |
                (xi_lngAccess * Convert.ToInt64((RaiseToPower(2, 14)))) |
                (xi_lngFunction * Convert.ToInt64((RaiseToPower(2, 2)))) |
                xi_lngMethod;

        }

        internal string GetDeviceName(Guid ID)
        {

            IntPtr hDevice = (IntPtr)0;
            string strDeviceName = "";
            bool blnSuccess = false;
            int intBytesReturned = 0;
            Guid GarminGUID = new Guid(GARMIN_GUID);

            //strDeviceName = @"\\?\usb#vid_091e&pid_0003#5&31afefbb&0&1#{2c9c45c2-8e7d-4c08-a12d-816bbae722c0}";

            Waymex.Interop.NativeMethods.DEVICE_INTERFACE_DATA DeviceInterfaceData = new Waymex.Interop.NativeMethods.DEVICE_INTERFACE_DATA();
            Waymex.Interop.NativeMethods.DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData = new Waymex.Interop.NativeMethods.DEVICE_INTERFACE_DETAIL_DATA(); ;

            //pass the guid top get a handle to the device
            //first param indicates the interface required
            //the last argument indicate we are looking for the interfaces exported by the USB I/O device

            hDevice = Waymex.Interop.NativeMethods.SetupDiGetClassDevs(ref GarminGUID, null, IntPtr.Zero, Waymex.Interop.NativeMethods.DIGCF_PRESENT | Waymex.Interop.NativeMethods.DIGCF_DEVICEINTERFACE);

            //check for errors
            if (hDevice.ToInt32() == -1)
                throw new USBTransportException(ERR_9005_MSG);

            //once we have a handle to the device driver we can enumerate through all the devices
            //that export the particular interface

            DeviceInterfaceData.cbsize = 28; //Length of data structure in bytes
            if (Waymex.Interop.NativeMethods.SetupDiEnumDeviceInterfaces(hDevice, IntPtr.Zero, ref GarminGUID, 0, ref DeviceInterfaceData))
            {

                // get the rquired buffer size
                blnSuccess = Waymex.Interop.NativeMethods.SetupDiGetDeviceInterfaceDetail(hDevice, ref DeviceInterfaceData, IntPtr.Zero, 0, ref intBytesReturned, IntPtr.Zero);

                // sort out the buffer
                IntPtr Buffer = Marshal.AllocHGlobal(intBytesReturned);

                DeviceInterfaceDetailData = new Waymex.Interop.NativeMethods.DEVICE_INTERFACE_DETAIL_DATA();
                DeviceInterfaceDetailData.cbsize = (uint)Marshal.SizeOf(typeof(Waymex.Interop.NativeMethods.DEVICE_INTERFACE_DETAIL_DATA));

                Marshal.StructureToPtr(DeviceInterfaceDetailData, Buffer, false);

                //second call to get the actual data
                if (Waymex.Interop.NativeMethods.SetupDiGetDeviceInterfaceDetail(hDevice, ref DeviceInterfaceData, Buffer, intBytesReturned, ref intBytesReturned, IntPtr.Zero))
                {
                    //retrieve the device path from DEVICE_INTERFACE_DETAIL_DATA
                    // 4 == the offset of first device path character
                    IntPtr pDevicePath = (IntPtr)((int)Buffer + 4);
                    strDeviceName = Marshal.PtrToStringAuto(pDevicePath);

                }
                else
                {
                    //IntPtr pDevicePath = (IntPtr){(int)Buffer + 4);
                    //strDeviceName = Marshal.PtrToStringAuto(pDevicePath);
                    throw new USBTransportException(ERR_9006_MSG);
                }

                // clean up
                Marshal.FreeHGlobal(Buffer);

            }
            else
            {
                Waymex.Interop.NativeMethods.SetupDiDestroyDeviceInfoList(hDevice);
                throw new USBTransportException(ERR_9006_MSG);
            }

            return strDeviceName;
        }

        //internal string GetDeviceName(Guid ID)
        //{

        //    IntPtr hDevice = (IntPtr)0;
        //    string strDeviceName = "";
        //    bool blnSuccess = false;
        //    int intBytesReturned = 0;
        //    Guid GarminGUID = new Guid(GARMIN_GUID);

        //    //strDeviceName = @"\\?\usb#vid_091e&pid_0003#5&31afefbb&0&1#{2c9c45c2-8e7d-4c08-a12d-816bbae722c0}";

        //    Waymex.Interop.NativeMethods.DEVICE_INTERFACE_DATA deviceInterfaceData = new Waymex.Interop.NativeMethods.DEVICE_INTERFACE_DATA();
        //    Waymex.Interop.NativeMethods.DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData = new Waymex.Interop.NativeMethods.DEVICE_INTERFACE_DETAIL_DATA();
        //    //Waymex.Interop.NativeMethods.SP_DEVINFO_DATA deviceInfoData = new Waymex.Interop.NativeMethods.SP_DEVINFO_DATA();
        //    //Waymex.Interop.NativeMethods.SP_DEVINFO_DATA deviceInfoData = (Waymex.Interop.NativeMethods.SP_DEVINFO_DATA)IntPtr.Zero;

        //    deviceInterfaceData.cbsize = (uint)Marshal.SizeOf(typeof(Waymex.Interop.NativeMethods.DEVICE_INTERFACE_DATA));
        //    deviceInterfaceDetailData.cbsize = (uint)Marshal.SizeOf(typeof(Waymex.Interop.NativeMethods.DEVICE_INTERFACE_DETAIL_DATA));
        //    //deviceInfoData.cbsize = (uint)Marshal.SizeOf(typeof(Waymex.Interop.NativeMethods.SP_DEVINFO_DATA));
        //    //deviceInfoData.ClassGuid = GarminGUID;

        //    //pass the guid top get a handle to the device
        //    //first param indicates the interface required
        //    //the last argument indicate we are looking for the interfaces exported by the USB I/O device
        //    hDevice = Waymex.Interop.NativeMethods.SetupDiGetClassDevs(ref GarminGUID, null, IntPtr.Zero, Waymex.Interop.NativeMethods.DIGCF_PRESENT | Waymex.Interop.NativeMethods.DIGCF_DEVICEINTERFACE);

        //    //check for errors
        //    if (hDevice.ToInt32() == -1)
        //        throw new USBTransportException(ERR_9005_MSG);

        //    //once we have a handle to the device driver we can enumerate through all the devices
        //    //that export the particular interface
        //    Waymex.Interop.ManagedMethods.SetupDiEnumDeviceInterfaces(hDevice, ref GarminGUID, 0, ref deviceInterfaceData);

        //    if (Waymex.Interop.NativeMethods.SetupDiEnumDeviceInterfaces(hDevice, IntPtr.Zero, ref GarminGUID, 0, ref deviceInterfaceData))
        //    {

        //        // get the rquired buffer size
        //        blnSuccess = Waymex.Interop.NativeMethods.SetupDiGetDeviceInterfaceDetail(hDevice, ref deviceInterfaceData, ref deviceInterfaceDetailData, 0, ref intBytesReturned, IntPtr.Zero);

        //        // sort out the buffer
        //        IntPtr buffer = Marshal.AllocHGlobal(intBytesReturned);
        //        Marshal.StructureToPtr(deviceInterfaceDetailData, buffer, false);

        //        //second call to get the actual data
        //        if (Waymex.Interop.NativeMethods.SetupDiGetDeviceInterfaceDetail(hDevice, ref deviceInterfaceData, ref deviceInterfaceDetailData, intBytesReturned, ref intBytesReturned, IntPtr.Zero))
        //        {
        //            //retrieve the device path from DEVICE_INTERFACE_DETAIL_DATA
        //            // 4 == the offset of first device path character
        //            IntPtr pDevicePath = (IntPtr)((int)buffer + 4);
        //            strDeviceName = Marshal.PtrToStringAuto(pDevicePath);

        //        }
        //        else
        //        {
        //            //IntPtr pDevicePath = (IntPtr){(int)Buffer + 4);
        //            //strDeviceName = Marshal.PtrToStringAuto(pDevicePath);
        //            throw new USBTransportException(ERR_9006_MSG);
        //        }

        //        // clean up
        //        Marshal.FreeHGlobal(buffer);

        //    }
        //    else
        //    {
        //        throw new USBTransportException(ERR_9006_MSG);
        //        Waymex.Interop.ManagedMethods.SetupDiDestroyDeviceInfoList(hDevice);
        //    }

        //    return strDeviceName;
        //}

        private void SetPIDValues(LinkProtocol Protocol)
        {
            //---------------------------------------------------------------------------
            //  DESCRIPTION:	This populates the Process ID array (mPIDData). This
            //                  is used throughout the module to translate the enumerated
            //                  PID value passed as arguments to the actual value required
            //                  depending on the protocol selected. This Procedure
            //                  is called when this class initialises to set default
            //                  values.
            //            
            //	PARAMETERS:
            //                  Protocol    Enumerated Integer representing L000,
            //                              L001 or L002.
            //    
            //  RETURNS:
            //                  <None>
            // 
            //---------------------------------------------------------------------------
            //

            try
            {
                //set the common L000/1/2 protocol values
                mPIDData[0] = 6; //ACK_Byte
                mPIDData[1] = 21; //NAK_Byte
                mPIDData[2] = 253; //Protocol_Array
                mPIDData[3] = 254; //Product_Rqst
                mPIDData[4] = 255; //Product_Data

                //set the rest to -1 (NOSUPPORT)
                for (int f = 5; f <= mPIDData.Length - 1; f++)
                {
                    mPIDData[f] = NOSUPPORT;
                }

                //determine the additional protocol values to use
                switch (Protocol)
                {
                    //
                    case LinkProtocol.L001://basic values plus these
                        {

                            mPIDData[5] = 10; //CommandData
                            mPIDData[6] = 12; //Xfer_Cmplt
                            mPIDData[7] = 14; //Date_Time
                            mPIDData[8] = 17; //PositionData
                            mPIDData[9] = 19; //ProximityWaypointData
                            mPIDData[10] = 27; //Records
                            mPIDData[11] = 29; //RouteHeader
                            mPIDData[12] = 30; //Rte_WaypointData
                            mPIDData[13] = 31; //AlmanacData
                            mPIDData[14] = 34; //TrackData
                            mPIDData[15] = 35; //WaypointData
                            mPIDData[16] = 51; //PvtData
                            mPIDData[17] = 98; //RouteLinkData
                            mPIDData[18] = 99; //TrackHeader
                            mPIDData[19] = 134; //FlghtBook data
                            mPIDData[20] = 149; //Lap data
                            mPIDData[21] = 152; //Waypoint Category
                            mPIDData[22] = 52; //Receiver Measurement
                            mPIDData[23] = 114; //Satellite data
                            mPIDData[24] = 48; //Baud Request Data
                            mPIDData[25] = 49; //Baud Accept Data

                            break;
                        }
                    case LinkProtocol.L002: //basic values plus these
                        {
                            mPIDData[5] = 11; //Command_Data
                            mPIDData[6] = 12; //Xfer_Cmplt
                            mPIDData[7] = 20; //Date_Time
                            mPIDData[8] = 24; //Position_Data
                            mPIDData[9] = NOSUPPORT; //Prx_Wpt_Data (not supported)
                            mPIDData[10] = 35; //Records
                            mPIDData[11] = 37; //Rte_Hdr
                            mPIDData[12] = 39; //Rte_Wpt_Data
                            mPIDData[13] = 4; //Almanac_Data
                            mPIDData[15] = 43; //Wpt_Data
                            mPIDData[16] = NOSUPPORT; //Pvt_Data (not supported)
                            mPIDData[17] = NOSUPPORT; //Rte_Link_Data (not supported)
                            mPIDData[18] = NOSUPPORT; //Trk_Hdr (not supported)
                            mPIDData[19] = NOSUPPORT;
                            mPIDData[20] = NOSUPPORT;
                            mPIDData[21] = NOSUPPORT;
                            mPIDData[22] = NOSUPPORT;
                            mPIDData[23] = NOSUPPORT;
                            mPIDData[24] = NOSUPPORT;
                            mPIDData[25] = NOSUPPORT;

                            break;
                        }
                }
            }
            catch
            {
                throw;
            }

        }

        private void CloseUsbDevice()
        {
            bool blnSuccess = false;
            int intError = 0;


            blnSuccess = Waymex.Interop.NativeMethods.CancelIo(deviceHandles.Device);
            if (!blnSuccess)
            {
                intError = Marshal.GetLastWin32Error();
                //throw new USBTransportException("CloseHandle returned the API Error: " + intError.ToString());
            }

            blnSuccess = Waymex.Interop.NativeMethods.CloseHandle(deviceHandles.Device);
            if (!blnSuccess)
            {
                intError = Marshal.GetLastWin32Error();
                //throw new USBTransportException("CloseHandle returned the API Error: " + intError.ToString());
            }

            blnSuccess = Waymex.Interop.NativeMethods.CloseHandle(deviceHandles.DevIoEvent);
            if (!blnSuccess)
            {
                intError = Marshal.GetLastWin32Error();
                //throw new USBTransportException("CloseHandle returned the API Error: " + intError.ToString());
            }

            blnSuccess = Waymex.Interop.NativeMethods.CloseHandle(deviceHandles.BulkReadEvent);
            if (!blnSuccess)
            {
                intError = Marshal.GetLastWin32Error();
                //throw new USBTransportException("CloseHandle returned the API Error: " + intError.ToString());
            }

            blnSuccess = Waymex.Interop.NativeMethods.CloseHandle(deviceHandles.BulkWriteEvent);
            if (!blnSuccess)
            {
                intError = Marshal.GetLastWin32Error();
                //throw new USBTransportException("CloseHandle returned the API Error: " + intError.ToString());
            }

            deviceHandles.Device = IntPtr.Zero;
            deviceHandles.DevIoEvent = IntPtr.Zero;
            deviceHandles.BulkReadEvent = IntPtr.Zero;
            deviceHandles.BulkWriteEvent = IntPtr.Zero;

        }

        private void OpenUsbDevice(System.Guid ID, string deviceName)
        {
            //DEVICE_HANDLES structDeviceHandles = new DEVICE_HANDLES();
            string strDeviceName = "";
            IntPtr lpSecurityAttributes = IntPtr.Zero;
            IntPtr hDevice = IntPtr.Zero;
            IntPtr hEventHandleDevIO = IntPtr.Zero;
            IntPtr hEventHandleRead = IntPtr.Zero;
            IntPtr hEventHandleWrite = IntPtr.Zero;
            Waymex.Interop.NativeMethods.SECURITY_ATTRIBUTES utSecurity = new Waymex.Interop.NativeMethods.SECURITY_ATTRIBUTES();
            int intError = 0;
            double dblTimer = 0;

            //initialise the structure for good measure
            deviceHandles.Device = IntPtr.Zero;
            deviceHandles.BulkReadEvent = IntPtr.Zero;
            deviceHandles.BulkWriteEvent = IntPtr.Zero;
            deviceHandles.DevIoEvent = IntPtr.Zero;

            //set the initial state to signalled as this means that the overlapped operation has completed
            lpSecurityAttributes = Marshal.AllocHGlobal(Marshal.SizeOf(utSecurity));

            Marshal.StructureToPtr(utSecurity, lpSecurityAttributes, true);
            hEventHandleDevIO = Waymex.Interop.NativeMethods.CreateEvent(lpSecurityAttributes, API_TRUE, API_FALSE, OVERLAPPED_EVENT_NAME_DEVICEIOCTRL);
            hEventHandleRead = Waymex.Interop.NativeMethods.CreateEvent(lpSecurityAttributes, API_TRUE, API_FALSE, OVERLAPPED_EVENT_NAME_READFILE);
            hEventHandleWrite = Waymex.Interop.NativeMethods.CreateEvent(lpSecurityAttributes, API_TRUE, API_FALSE, OVERLAPPED_EVENT_NAME_WRITEFILE);
            Marshal.FreeHGlobal(lpSecurityAttributes);

            //get the name of the device
            if (deviceName == null || deviceName == string.Empty)
                strDeviceName = GetDeviceName(ID);
            else
                strDeviceName = deviceName;

            utSecurity.nLength = 12; //length in bytes of the structure (2 x Int)
            utSecurity.bInheritHandle = API_TRUE;

            //check to ensure device is connected
            if (strDeviceName.Length > 0)
            {
                lpSecurityAttributes = Marshal.AllocHGlobal(Marshal.SizeOf(utSecurity));
                Marshal.StructureToPtr(utSecurity, lpSecurityAttributes, true);

                //example from Garmin SDK
                //gHandle = CreateFile(theDevDetailData->DevicePath,GENERIC_READ | GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL );

                //hDevice = NativeMethods.CreateFile(strDeviceName, FILE_SHARE_WRITE | FILE_SHARE_READ, 0, ptrPointer, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, IntPtr.Zero);
                dblTimer = Timer();

                while (true)
                {
                    hDevice = Waymex.Interop.NativeMethods.CreateFile(strDeviceName, GENERIC_READ | GENERIC_WRITE, 0, lpSecurityAttributes, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, IntPtr.Zero);
                    if (hDevice.ToInt32() == INVALID_HANDLE_VALUE)
                    {
                        intError = Marshal.GetLastWin32Error();
                    }
                    else
                    {
                        break;
                    }

                    if (Timer() >= dblTimer + OPEN_USB_TIMEOUT)
                        throw new USBTransportException(ERR_9001_MSG);
                }

                deviceHandles.Device = hDevice;
                deviceHandles.DevIoEvent = hEventHandleDevIO;
                deviceHandles.BulkReadEvent = hEventHandleRead;
                deviceHandles.BulkWriteEvent = hEventHandleWrite;
                deviceHandles.Overlapped = true;

                Marshal.FreeHGlobal(lpSecurityAttributes);

            }
            else
            {
                throw new USBTransportException(ERR_9000_MSG);
            }
            //return structDeviceHandles;
        }

        //internal void PortOpen()
        //{
        //    PortOpen(string.Empty);
        //}
        internal void PortOpen(string deviceName)
        {

            //close any open ports first
            //CloseUsbDevice();
            try
            {
                //open the usb device
                OpenUsbDevice(new Guid(GARMIN_GUID), deviceName);

                //start the receive threads
                InterruptInThread = new Thread(new ThreadStart(this.ReadInterruptIn));
                InterruptInThread.Name = "InterruptIn";
                InterruptInThread.Priority = ThreadPriority.Normal;
                InterruptInThread.Start();

                BulkInThread = new Thread(new ThreadStart(this.ReadBulkIn));
                BulkInThread.Name = "BulkIn";
                BulkInThread.Priority = ThreadPriority.Normal;
                BulkInThread.Start();

                //reset the bulk in flage in order to read from Interrupt In
                SwitchToInterruptIn();

                //start a session
                StartSession();
            }
            catch (Exception e)
            {
                throw new USBTransportException(ERR_9000_MSG);
            }

        }
        internal void PortClose()
        {
            //stop the receive thread
            if (InterruptInThread != null)
                InterruptInThread.Abort();
            if (BulkInThread != null)
                BulkInThread.Abort();

            CloseUsbDevice();

            //thread wait here to allow things to clear down
            Thread.Sleep(500);

        }
        internal void ClearBuffer()
        {
            //clear both the Bulk In and Interrupt In buffers
            m_bufferBI.Clear();
            m_bufferINT.Clear();
            //mlngBIBufferInPtr = mlngBIBufferOutPtr;
            //mlngINTBufferInPtr = mlngINTBufferOutPtr;
        }

        private void StartSession()
        {

            clsUsbPacket objUsbPacket = new clsUsbPacket();

            byte[] bytData = new byte[12];
            byte[] bytTemp = null;
            byte[] bytUsbPacket = new byte[21]; //21 bytes in the header + the data
            bool blnSuccess = false;

            //clear the buffers
            ClearBuffer();

            //create the 12 byte session start packet
            bytData[0] = PACKET_TYPE_TRANSPORT;
            bytData[1] = 0;
            bytData[2] = 0;
            bytData[3] = 0;
            bytData[4] = START_SESSION_PACKET_ID;
            bytData[5] = 0;
            bytData[6] = 0;
            bytData[7] = 0;
            bytData[8] = 0;
            bytData[9] = 0;
            bytData[10] = 0;
            bytData[11] = 0;

            TraceLog.WriteMessage(string.Concat("Transmit Data BO : ", ByteToHex(bytData), " (", ByteToAsc(bytData), ")"), TraceLevel.Verbose, true);

            //write the packet to Bulk Out
            objReadWriteLock.AcquireReaderLock(-1);
            blnSuccess = WriteBulkOut(bytData, deviceHandles);
            objReadWriteLock.ReleaseReaderLock();

            //read interupt in until the session started packet arrives or we time out
            objUsbPacket = GetINTPacket();

            bytTemp = PacketToByte(objUsbPacket);

            if (bytTemp.Length > 0)
                TraceLog.WriteMessage(string.Concat("Receive Data  INT: ", ByteToHex(bytTemp), " (", ByteToAsc(bytTemp), ")"), TraceLevel.Verbose, true);

            //if session not started then raise error
            if (!(objUsbPacket.PacketType == usbPacketType.usbPacketTypeTransport && objUsbPacket.PacketID == SESSION_STARTED_PACKET_ID))
            {
                throw new USBTransportException(ERR_9004_MSG);
            }

        }
        internal LinkProtocol Protocol
        {
            //---------------------------------------------------------------------------
            //   DESCRIPTION:    Sets the module level variable to indicate the required
            //                   link protocol and calls the setPIDValues procedure to
            //                   set the actual pid values for the protocol selected.
            //
            //
            //   PARAMETERS:     iProt   Enumerated integer indicating the required link
            //                           level protocol, L000, L001 or L002.
            //
            //
            //   RETURNS:        <none>
            //
            //
            //---------------------------------------------------------------------------

            set
            {
                try
                {
                    //save the value for the get property
                    miProtocol = value;

                    //use a function to set the actual values so that the same function can
                    //be called in the initialise event with a default value
                    SetPIDValues(miProtocol);
                }
                catch
                {
                    throw;
                }
            }
        }
        private bool WriteBulkOut(byte[] data, DeviceHandles deviceHandles)
        {
            bool success = false;

            success = Waymex.Interop.ManagedMethods.WriteFileOverlapped(deviceHandles.Device, deviceHandles.BulkWriteEvent, data, BOUT_TIMEOUT);

            return success;
        }

        private clsUsbPacket GetINTPacket()
        {
            clsUsbPacket objUsbPacket = new clsUsbPacket();

            bool blnPktFound = false;
            bool blnPktStartFound = false;
            bool blnTimeout = false;
            byte[] bytPacket = null;
            byte readByte;
            byte[] bytTemp = null;
            //byte[] bytEmptyPacket		= null;
            int intByte = 0;
            //byte bytTemp				= 0;
            System.DateTime dtStart = DateTime.Now;
            double dblStart = 0;
            int intDataSize = 0;


            //Debug.WriteLine("Looking for Packet on IntIn Pipe");

            try
            {
                //create the basic size packet we can always increase it in size later
                bytPacket = new byte[STANDARD_PACKET_SIZE];

                //no data in packet array as yet
                intByte = -1;
                blnPktFound = false;
                blnPktStartFound = false;

                //set the start time
                dblStart = Timer();

                //objReadWriteLock.AcquireReaderLock(-1);

                //run through buffer looking for a packet starting at mlngINTBufferOutPtr
                while (!(blnPktFound || blnTimeout))
                {
                    //only check for packet if buffer is not empty
                    //objReadWriteLock.AcquireReaderLock(-1);

                    if (m_bufferINT.Count > 0)
                    //if (mlngINTBufferOutPtr != mlngINTBufferInPtr)
                    {

                        //Debug.WriteLine("Looking for Packet");
                        System.Windows.Forms.Application.DoEvents();

                        //have we found the packet start
                        if (!blnPktStartFound)
                        {
                            readByte = m_bufferINT.ReadByte();
                            if (readByte == PACKET_TYPE_TRANSPORT || readByte == PACKET_TYPE_APPLICATION)
                                //if (mbytINTBuffer[mlngINTBufferOutPtr] == PACKET_TYPE_TRANSPORT || mbytINTBuffer[mlngINTBufferOutPtr] == PACKET_TYPE_APPLICATION)
                                blnPktStartFound = true;
                        }

                        if (blnPktStartFound)
                        {
                            //reset start time as data was found
                            dblStart = Timer();

                            //start of packet found so build up packet
                            intByte = intByte + 1;

                            bytPacket[intByte] = m_bufferINT.RemoveByte();

                            //check data size and therefore how many moe bytes to read
                            if (intByte == 11) //data size integer has been received byte 8-11
                            {
                                //get data size
                                intDataSize = ByteToInt(bytPacket, 8); //byte 8 is where the long value is stored

                                if (intDataSize > 0)
                                {
                                    //increase the size of the packet array accordingly
                                    bytPacket = ReDimPreserve(bytPacket, STANDARD_PACKET_SIZE + intDataSize);
                                }
                            }

                            //check to see if we have found the packet
                            if (intByte >= 11 && intByte == intDataSize + 11)
                            {
                                blnPktFound = true;
                                break;
                            }
                        }
                        else
                        {
                            //not a USB or Application packet
                            throw new USBTransportException(ERR_9007_MSG);
                        }

                    }	//mlngINTBufferOutPtr != mlngINTBufferInPtr


                    //check timeout to see if we have been waiting too long (not required on non overlapped operations)
                    if (Timer() > (dblStart + READ_INT_BUFFER_TIMEOUT))
                        blnTimeout = true;

                }

                //tidy the packet
                if (blnPktFound)
                    objUsbPacket = ByteToPacket(bytPacket);
                else
                    objUsbPacket.PacketType = usbPacketType.usbPacketTypeUnknown;

                //set the source
                objUsbPacket.PacketSource = usbPacketSource.usbPacketSourceInterruptIn;

                //convert to byte array and log to Trace file if valid usb packet
                if (objUsbPacket.PacketType != usbPacketType.usbPacketTypeUnknown)
                {
                    bytTemp = PacketToByte(objUsbPacket);

                    if (bytTemp.Length > 0)
                        TraceLog.WriteMessage(string.Concat("Receive Data  INT: ", ByteToHex(bytTemp), " (", ByteToAsc(bytTemp), ")"), TraceLevel.Verbose, true);
                }

            }
            catch (Exception e)
            {
                throw new USBTransportException(e);

            }
            finally
            {
                if (objReadWriteLock.IsReaderLockHeld)
                    objReadWriteLock.ReleaseReaderLock();
                if (objReadWriteLock.IsWriterLockHeld)
                    objReadWriteLock.ReleaseWriterLock();
            }


            return objUsbPacket;

        }

        private void ReadInterruptIn()
        {

            IntPtr lpBuffer = IntPtr.Zero;

            try
            {
                while (true)
                {

                    //check device id
                    if (deviceHandles.Device == IntPtr.Zero)
                        throw new USBTransportException(ERR_9003_MSG);

                    lpBuffer = Marshal.AllocHGlobal((int)GARMIN_USB_INTERRUPT_DATA_SIZE);

                    byte[] data = Waymex.Interop.ManagedMethods.ReadDeviceIoControlOverlapped(
                        deviceHandles.Device,
                        lpBuffer,
                        GARMIN_USB_INTERRUPT_DATA_SIZE,
                        deviceHandles.DevIoEvent,
                        IOCTL_GARMIN_USB_API_INTERRUPT_IN,
                        IO_TIMEOUT);

                    if (data != null)
                        m_bufferINT.Add(data);


                }
            }
            catch (ThreadAbortException e)
            {
            }
            finally
            {
                Marshal.FreeHGlobal(lpBuffer);
            }

        }
        private static double Timer()
        {
            DateTime dtTimer;

            dtTimer = DateTime.Now;

            //Debug.Write("Timer() ");     
            //Debug.WriteLine(dtTimer.Ticks % 711573504 / 1e+007);
            return (double)dtTimer.Ticks % 711573504 / 1e+007;
        }

        private double RaiseToPower(double dblValue, double dblPower)
        {
            double dblRetval = dblValue;
            for (int i = 0; i < dblPower - 1; i++)
            {
                dblRetval *= dblValue;
            }
            return dblRetval;
        }

        private static byte[] ReDimPreserve(byte[] bytArray, int intNewDimension)
        {
            //re-dimensions a byte array and preserves the contents
            if (intNewDimension <= 0 || intNewDimension == 0)
                throw new ArgumentException(ERR_3010_MSG);

            byte[] bytNewArray = new byte[intNewDimension];

            //copy any existing data
            if (bytArray != null)
                System.Array.Copy(bytArray, 0, bytNewArray, 0, bytArray.Length);

            return bytNewArray;
        }

        private static string[] ReDimPreserveS(string[] strArray, int intNewDimension)
        {
            //re-dimensions a byte array and preserves the contents
            if (intNewDimension <= 0 || intNewDimension == 0)
                throw new ArgumentException(ERR_3011_MSG);

            string[] strNewArray = new string[intNewDimension];

            //copy any existing data
            if (strArray != null)
                System.Array.Copy(strArray, 0, strNewArray, 0, strArray.Length);

            return strNewArray;
        }

        private int ByteToInt(byte[] bytData, int iPosition)
        {

            const int DATA_SIZE = 4;

            int intData = 0;
            try
            {
                //check that the position is OK
                if (iPosition <= (bytData.GetUpperBound(0) - (DATA_SIZE - 1)))
                    intData = BitConverter.ToInt32(bytData, iPosition);
            }
            catch
            {
                throw;
            }
            return intData;
        }

        private clsUsbPacket ByteToPacket(byte[] bytPacket)
        {
            clsUsbPacket usbPacket = new clsUsbPacket();

            try
            {
                if (bytPacket.Length >= 12)
                {
                    switch (bytPacket[0])
                    {
                        //                  
                        case 0:
                            //transport packet
                            usbPacket.PacketType = usbPacketType.usbPacketTypeTransport;
                            break;
                        case 20:
                            //application packet
                            usbPacket.PacketType = usbPacketType.usbPacketTypeApplication;
                            break;
                        default:
                            //unknownpacket
                            usbPacket.PacketType = usbPacketType.usbPacketTypeUnknown;
                            break;
                    }

                    usbPacket.PacketID = (short)ByteToInt(bytPacket, 4);
                    usbPacket.DataSize = ByteToInt(bytPacket, 8);

                    //add data array
                    if (bytPacket.Length > 12)
                    {
                        for (int intIndex = 12; intIndex < bytPacket.Length; intIndex++)
                        {
                            usbPacket.Data = ReDimPreserve(usbPacket.Data, usbPacket.DataSize);
                            usbPacket.Data[intIndex - 12] = bytPacket[intIndex];
                        }
                    }
                }
                else
                {
                    //not a valid packet
                    usbPacket.PacketType = usbPacketType.usbPacketTypeUnknown;
                }
            }

            catch (Exception e)
            {
                //need to ignore subscript out of range exceptions and resume next?
                throw new USBTransportException(e);
            }

            return usbPacket;
        }

        private GpsPidData USBPacketToPIDData(clsUsbPacket usbPacket)
        {

            GpsPidData PData = new GpsPidData();

            PData.Pid = LookupPID((byte)usbPacket.PacketID);

            //now the data portion
            if (usbPacket.DataSize > 0)
            {
                for (int f = 0; f < usbPacket.DataSize; f++)
                {
                    PData.ByteData = ReDimPreserve(PData.ByteData, usbPacket.DataSize);
                    if (usbPacket.Data != null)
                        PData.ByteData[f] = usbPacket.Data[f];
                }
            }
            return PData;
        }

        private clsUsbPacket PIDDataToUSBPacket(GpsPidData PData)
        {
            clsUsbPacket usbPacket = new clsUsbPacket();
            int intDataBytes = 0;

            //get the size of the data
            if (PData.ByteData != null)
                intDataBytes = PData.ByteData.Length;
            else
                intDataBytes = 0;

            //need to convert the PData type to a USB packet
            usbPacket.PacketType = usbPacketType.usbPacketTypeApplication; //all pid data comes from application layer
            usbPacket.PacketID = (short)mPIDData[(int)PData.Pid];
            if (intDataBytes > 0)
                usbPacket.Data = PData.ByteData;
            usbPacket.DataSize = intDataBytes;

            return usbPacket;

        }

        internal bool TransmitData(GpsPidData PData)
        {

            byte[] bytData = null;
            byte[] bytTemp = null;

            //create an array for logging purposes
            bytTemp = new byte[1];

            bytTemp[0] = (byte)mPIDData[(int)PData.Pid];

            bytTemp = ByteConcat(bytTemp, PData.ByteData);

            //create a packet for transmission
            bytData = PacketToByte(PIDDataToUSBPacket(PData));

            TraceLog.WriteMessage(string.Concat("Transmit Data BO : ", ByteToHex(bytData), " (", ByteToAsc(bytData), ")"), TraceLevel.Verbose, true);

            //transmit the data to bulk out
            objReadWriteLock.AcquireReaderLock(-1);
            WriteBulkOut(bytData, deviceHandles);
            objReadWriteLock.ReleaseReaderLock();

            return true;
        }

        private static string ByteToHex(byte[] ByteData)
        {
            //---------------------------------------------------------------------------
            //	DESCRIPTION:	This function converts an array of bytes to a string
            //					of hex digits representing those bytes.
            //
            //
            //	PARAMETERS:
            //				ByteData()  Array of bytes.
            //
            //	RETURNS:
            //				String      String of hex character pairs representing
            //							each byte in the byte array, separated
            //							with a space.
            //
            //---------------------------------------------------------------------------

            string sTemp = "";
            string sMessage = "";

            try
            {
                if (ByteData != null)
                {
                    for (int f = 0; f <= ByteData.GetUpperBound(0); f++)
                    {
                        sTemp = ByteData[f].ToString("X"); //hex returns a string representing the passed number

                        if (sTemp.Length < 2)
                        {
                            sTemp = string.Concat("0", sTemp);
                        }

                        sMessage = string.Concat(sMessage, " ", sTemp);
                    }

                    return sMessage.Trim();
                }
                else
                {
                    return "";
                }
            }
            catch
            {
                throw;
            }
        }

        private static string ByteToAsc(byte[] ByteData)
        {
            //---------------------------------------------------------------------------
            //   DESCRIPTION:    This function converts an array of bytes to a string
            //                   of ascii characters representing those bytes.
            //
            //
            //   PARAMETERS:
            //                   ByteData()  Array of bytes.
            //
            //   RETURNS:
            //                   String      String of ascii character representing
            //                               each byte in the byte array, an invalid
            //                               ascii code is represented by a ".".
            //
            //---------------------------------------------------------------------------

            char chrTemp = '.';
            string sMessage = "";

            try
            {
                if (ByteData != null)
                {
                    for (int f = 0; f <= ByteData.GetUpperBound(0); f++)
                    {
                        if ((ByteData[f] >= 32) && (ByteData[f] <= 127))
                        {
                            chrTemp = Convert.ToChar(ByteData[f]);
                        }
                        else
                        {
                            chrTemp = '.';
                        }

                        sMessage = string.Concat(sMessage, chrTemp.ToString());

                    }
                }
            }
            catch
            {
                throw;
            }

            return sMessage.Trim();

        }

        internal GpsPidData ReceiveData()
        {

            clsUsbPacket usbPacket = new clsUsbPacket();
            GpsPidData PData = new GpsPidData();


            try
            {
                if (mblnReadBulkIn)
                {

                    //read Bulk In try three times before giving up
                    for (int intCount = 0; intCount < 3; intCount++)
                    {
                        usbPacket = GetBIPacket();
                        if (usbPacket.PacketType != usbPacketType.usbPacketTypeUnknown)
                            break;
                    }

                    if (usbPacket.PacketType == usbPacketType.usbPacketTypeUnknown ||
                        (usbPacket.PacketType == usbPacketType.usbPacketTypeTransport &&
                        usbPacket.PacketID == 0))
                    {
                        //no longer reading BI
                        SwitchToInterruptIn();
                    }

                }
                else
                {

                    //read Interrupt In try three times
                    for (int intCount = 0; intCount < 3; intCount++)
                    {
                        usbPacket = GetINTPacket();
                        if (usbPacket.PacketType != usbPacketType.usbPacketTypeUnknown)
                            break;
                    }

                    //see if we should now start reading bulk in
                    if (usbPacket.PacketType == usbPacketType.usbPacketTypeTransport && usbPacket.PacketID == BULK_DATA_AVAILABLE_PACKET_ID)
                    {

                        //no longer reading Int In
                        SwitchToBulkIn();

                        //read the first packet from bulk in yry three times
                        for (int intCount = 0; intCount < 3; intCount++)
                        {
                            usbPacket = GetBIPacket();
                            if (usbPacket.PacketType != usbPacketType.usbPacketTypeUnknown)
                                break;
                        }

                    }
                }

                //convert packet to GpsPidData
                if (usbPacket.PacketType == usbPacketType.usbPacketTypeApplication)
                {
                    PData = USBPacketToPIDData(usbPacket);
                }
                else
                {
                    PData.Pid = ProcessId.Null;
                    PData.ByteData = null;
                }
            }
            catch
            {
                throw;
            }
            return PData;

        }
        private void SwitchToBulkIn()
        {
            //			TraceLog.WriteMessage("Switching to Bulk In", TraceLevel.Verbose, true);
            objReadWriteLock.AcquireWriterLock(-1);
            mblnReadBulkIn = true;
            objReadWriteLock.ReleaseWriterLock();

            InterruptInThread.Priority = ThreadPriority.BelowNormal;
            BulkInThread.Priority = ThreadPriority.Normal;

        }
        private void SwitchToInterruptIn()
        {
            //			TraceLog.WriteMessage("Switching to Interrupt In", TraceLevel.Verbose, true);
            objReadWriteLock.AcquireWriterLock(-1);
            mblnReadBulkIn = false;
            objReadWriteLock.ReleaseWriterLock();

            BulkInThread.Priority = ThreadPriority.BelowNormal;
            InterruptInThread.Priority = ThreadPriority.Normal;
        }

        private byte[] IntToByte(int lData)
        {

            const int DATA_SIZE = 4;

            byte[] bytData = new byte[DATA_SIZE];

            try
            {
                return BitConverter.GetBytes(lData);
            }
            catch
            {
                throw;
            }
        }
        private static byte[] ShortToByte(short iData)
        {
            return ShortToByte(iData, false);
        }

        private static byte[] ShortToByte(short shtData, bool bSingleByte)
        {

            byte[] bytData;

            try
            {
                if (bSingleByte)
                {
                    bytData = new byte[1];
                    bytData[0] = (byte)(shtData % 256); //modulus (remainder)
                }
                else
                {
                    bytData = new byte[2];
                    bytData[0] = (byte)(shtData % 256);
                    bytData[1] = (byte)((int)shtData / (int)256); //div (integer division)
                }
            }
            catch
            {
                throw;
            }
            return bytData;
        }
        private byte[] ByteConcat(byte[] array1, byte[] array2)
        {

            byte[] data = null;
            int lenData1 = 0;
            int lenData2 = 0;

            try
            {
                //determine array lengths
                if (array1 != null)
                    lenData1 = array1.Length;
                if (array2 != null)
                    lenData2 = array2.Length;

                //create a new array to hold both incomming arrays
                data = new byte[lenData1 + lenData2];

                //copy the arrays
                if (lenData1 > 0)
                    Array.Copy(array1, 0, data, 0, lenData1);
                if (lenData2 > 0)
                    Array.Copy(array2, 0, data, lenData1, lenData2);

            }
            catch
            {
                //TraceLog.WriteError(e)
                throw;
            }

            return data;

        }
        private byte[] PacketToByte(clsUsbPacket usbPacket)
        {
            // byte sizes for usb packet
            //--------------------------------------
            // PacketType	= 2 bytes	(integer)
            // Reserved _A	= 3 bytes	(byte array)
            // PacketID		= 2 bytes	(integer)
            // Reserved_B	= 2 bytes	(byte array)
            // DataSize		= 4 bytes	(integer)
            // Data			= variable	(byte array)


            byte[] bytTemp = null;
            bytTemp = ByteConcat(bytTemp, ShortToByte((short)usbPacket.PacketType, true));
            bytTemp = ByteConcat(bytTemp, usbPacket.Reserved_A);
            bytTemp = ByteConcat(bytTemp, ShortToByte(usbPacket.PacketID));
            bytTemp = ByteConcat(bytTemp, usbPacket.Reserved_B);
            bytTemp = ByteConcat(bytTemp, IntToByte(usbPacket.DataSize));
            bytTemp = ByteConcat(bytTemp, usbPacket.Data);

            return bytTemp;

        }
        private void ReadBulkIn()
        {

            //const uint BYTES_TO_READ = GARMIN_USB_MAX_BUFFER_SIZE;
            const uint BYTES_TO_READ = 64;
            IntPtr lpBuffer = IntPtr.Zero;

            try
            {
                //check device id
                if (deviceHandles.Device == IntPtr.Zero)
                    throw new USBTransportException(ERR_9003_MSG);

                lpBuffer = Marshal.AllocHGlobal((int)BYTES_TO_READ);

                while (true)
                {
                    byte[] data = (Waymex.Interop.ManagedMethods.ReadFileOverlapped(deviceHandles.Device, lpBuffer, BYTES_TO_READ, deviceHandles.BulkReadEvent, IO_TIMEOUT));

                    if (data != null)
                        m_bufferBI.Add(data);

                }
            }
            catch (ThreadAbortException e)
            {
            }
            finally
            {

                Marshal.FreeHGlobal(lpBuffer);

                if (objReadWriteLock.IsReaderLockHeld)
                    objReadWriteLock.ReleaseReaderLock();
                if (objReadWriteLock.IsWriterLockHeld)
                    objReadWriteLock.ReleaseWriterLock();

            }
        }

        private ProcessId LookupPID(byte bPid)
        {
            //---------------------------------------------------------------------------
            //   DESCRIPTION:    This function converts a PID to the enumerated
            //                   equivelent. Internally (i.e. when passing PID's between
            //                   application layer and link layer PIDs are represented as
            //                   enumerated integer. This is then converted to the actual
            //                   PID value in the link layer depending upon the protocol
            //                   property. This function does the reverse by returning
            //                   the integer value of the PID based on the actual PID
            //                   value passed and the protocol curently set.
            //
            //   PARAMETERS:
            //                   bPID        Byte containing actual PID
            //
            //   RETURNS:
            //                   ProcessId   Enumerated integer representing the internal
            //                               protocol independent PID value. If the
            //                               PID cannot be found, -1 is returned
            //
            //---------------------------------------------------------------------------
            //
            //process array should always be populated with valid data

            int f = 0;
            //short iPid = 0;

            ProcessId nResult = ProcessId.Null;

            try
            {
                //run through module level array looking for first occurence of actual Pid
                for (f = 0; f <= mPIDData.GetUpperBound(0); f++)
                {
                    if (mPIDData[f] == (short)bPid)
                    {
                        nResult = (ProcessId)f;
                        break;
                    }
                    else
                    {
                        nResult = ProcessId.Null;
                    }
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            return nResult;
        }

        private clsUsbPacket GetBIPacket()
        {

            bool blnPktFound = false;
            bool blnPktStartFound = false;
            bool blnTimeout = false;
            byte[] bytPacket = null;
            int intByte = 0;
            byte[] bytTemp = null;
            //			datetime dtStart             As Date
            double dblStart = 0;
            int intDataSize = 0;
            clsUsbPacket usbPacket = new clsUsbPacket();
            byte readByte;

            //Debug.WriteLine("Looking for Packet on BulkIn Pipe");

            try
            {
                //create the basic size packet we can always increase it in size later
                bytPacket = new byte[STANDARD_PACKET_SIZE];

                //no data in packet array as yet
                intByte = -1;
                blnPktFound = false;
                blnPktStartFound = false;

                //set the start time
                dblStart = Timer();

                //run through buffer looking for a packet starting at mlngBIBufferOutPtr
                while (!(blnPktFound || blnTimeout))
                {

                    //only check for packet if buffer is not empty
                    //objReadWriteLock.AcquireReaderLock(-1);
                    if (m_bufferBI.Count > 0)
                    //if (mlngBIBufferOutPtr != mlngBIBufferInPtr)
                    {

                        //Debug.WriteLine("Looking for Packet");
                        System.Windows.Forms.Application.DoEvents();

                        //have we found the packet start
                        readByte = m_bufferBI.ReadByte();
                        if (readByte == PACKET_TYPE_TRANSPORT || readByte == PACKET_TYPE_APPLICATION)
                            //if (mbytBIBuffer[mlngBIBufferOutPtr] == PACKET_TYPE_TRANSPORT || mbytBIBuffer[mlngBIBufferOutPtr] == PACKET_TYPE_APPLICATION)
                            blnPktStartFound = true;

                        if (blnPktStartFound)
                        {
                            //reset start time as data was found
                            dblStart = Timer();

                            //start of packet found so build up packet
                            intByte = intByte + 1;

                            bytPacket[intByte] = m_bufferBI.RemoveByte();

                            //check data size and therefore how many moe bytes to read
                            if (intByte == 11) //data size integer has been received byte 8-11
                            {
                                //get data size
                                intDataSize = ByteToInt(bytPacket, 8); //byte 8 is where the long value is stored

                                if (intDataSize > 0)
                                {
                                    //increase the size of the packet array accordingly
                                    bytPacket = ReDimPreserve(bytPacket, STANDARD_PACKET_SIZE + intDataSize);
                                }
                            }

                            //check to see if we have found the packet
                            if (intByte >= 11 && intByte == intDataSize + 11)
                            {
                                blnPktFound = true;
                                break;
                            }
                        }
                        else
                        {
                            throw new USBTransportException(ERR_9007_MSG);
                        }//blnPktStartFound

                    }	//mlngINTBufferOutPtr <> mlngINTBufferInPtr

                    //check timeout to see if we have been waiting too long (not required on non overlapped operations)
                    if (Timer() > (dblStart + READ_BI_BUFFER_TIMEOUT))
                        blnTimeout = true;

                }


                //return the packet
                if (blnPktFound)
                    usbPacket = ByteToPacket(bytPacket);
                else
                    usbPacket.PacketType = usbPacketType.usbPacketTypeUnknown;

                //set the source
                usbPacket.PacketSource = usbPacketSource.usbPacketSourceBulkIn;

                //convert to byte array and log to Trace file if valid usb packet
                if (usbPacket.PacketType != usbPacketType.usbPacketTypeUnknown)
                {
                    bytTemp = PacketToByte(usbPacket);

                    if (bytTemp.Length > 0)
                        TraceLog.WriteMessage(string.Concat("Receive Data  BI : ", ByteToHex(bytTemp), " (", ByteToAsc(bytTemp), ")"), TraceLevel.Verbose, true);
                }


            }
            catch (Exception e)
            {
                throw new USBTransportException(e);

            }
            finally
            {
                if (objReadWriteLock.IsReaderLockHeld)
                    objReadWriteLock.ReleaseReaderLock();
                if (objReadWriteLock.IsWriterLockHeld)
                    objReadWriteLock.ReleaseWriterLock();
            }



            return usbPacket;

        }
    }
}
//using System;
//using System.Runtime.InteropServices;
//using System.Text;
//using System.Threading;
//using System.IO;
//using System.Xml.Serialization;
//using System.Diagnostics;
//using Waymex.Diagnostics;
//using Waymex.Buffers;

//namespace Waymex.Gps.Garmin
//{
//    /// <summary>
//    /// Summary description for GPSUSBTransport.
//    /// </summary>
//    internal class UsbTransport : System.IDisposable
//    {
//        // error constants
//        private const string ERR_9000_MSG = "GPS Device not detected on Universal Serial Bus.";
//        private const string ERR_9001_MSG = "Unable to obtain handle to USB Device.";
//        private const string ERR_9002_MSG = "Unable to read USB Interrupt In.";
//        private const string ERR_9003_MSG = "Invalid Device ID used to Read Data.";
//        private const string ERR_9004_MSG = "Unable to start a session with the USB Device.";
//        private const string ERR_9005_MSG = "Unable to find USB Device Driver.";
//        private const string ERR_9006_MSG = "Unable to get the name of the Device.";
//        private const string ERR_9007_MSG = "Packet Start not found in buffer.";
//        private const string ERR_9008_MSG = "Timed out during a Write Operation.";
//        private const string ERR_9009_MSG = "Timed out during a Read Operation.";
//        private const string ERR_3010_MSG = "The argument must be greater than zero.";
//        private const string ERR_3011_MSG = "The argument length must be greater than zero.";
//        private const string ERR_3012_MSG = "CreateFile returned the API Error: ";
//        private const string ERR_3013_MSG = "CloseHandle returned the API Error: ";

//        //usb guid
//        private const string GARMIN_GUID = "2C9C45C2-8E7D-4C08-A12D-816BBAE722C0";

//        // file constants
//        private const UInt32 FILE_ANY_ACCESS = 0;
//        private const UInt32 FILE_DEVICE_UNKNOWN = 0x22;
//        private const UInt32 FILE_READ_ACCESS = 0x1;
//        private const UInt32 FILE_WRITE_ACCESS = 0x2;
//        private const UInt32 FILE_SHARE_READ = 0x1;
//        private const UInt32 FILE_SHARE_WRITE = 0x2;
//        private const UInt32 FILE_SHARE_DELETE = 0x4;
//        private const UInt32 FILE_FLAG_OVERLAPPED = 0x40000000;
//        private const UInt32 FILE_ATTRIBUTE_NORMAL = 0x80;
//        private const UInt32 GENERIC_WRITE = 0x40000000;
//        private const UInt32 GENERIC_READ = 0x80000000;
//        private const UInt32 CREATE_ALWAYS = 0x2;
//        private const UInt32 OPEN_EXISTING = 0x3;
//        private const UInt32 OPEN_ALWAYS = 0x4;

//        private const UInt32 FORMAT_MESSAGE_FROM_SYSTEM = 0x1000;
//        private const Int32 INVALID_HANDLE_VALUE = -1;

//        private const Int32 METHOD_BUFFERED = 0;
//        private const Int32 METHOD_IN_DIRECT = 1;
//        private const Int32 METHOD_OUT_DIRECT = 2;
//        private const Int32 METHOD_NEITHER = 3;


//        private const Int32 INTERRUPT_IN_BUFFER_SIZE = 8000000;
//        private const Int32 BULK_IN_BUFFER_SIZE = 8000000;
//        private const Int32 STANDARD_PACKET_SIZE = 12;

//        private const string OVERLAPPED_EVENT_NAME_DEVICEIOCTRL = "USBGPSDeviceIOCtrlEvent";
//        private const string OVERLAPPED_EVENT_NAME_READFILE = "USBGPSReadFileEvent";
//        private const string OVERLAPPED_EVENT_NAME_WRITEFILE = "USBGPSWriteFileEvent";

//        private const int IO_TIMEOUT = 8;  //timeout waiting for the GetOverlappedResult api
//        private const int BOUT_TIMEOUT = 8;  //timeout waiting for the GetOverlappedResult api
//        private const int BIN_TIMEOUT = 1;  //timeout waiting for the GetOverlappedResult api
//        private const int OPEN_USB_TIMEOUT = 2;  //timeout waiting for create file to complete

//        private const int GARMIN_USB_API_VERSION = 1;
//        private const int GARMIN_USB_MAX_BUFFER_SIZE = 4096;
//        private const int GARMIN_USB_INTERRUPT_DATA_SIZE = 64;

//        //usb packet type constants (can't use enum as type must be byte
//        private const byte PACKET_TYPE_TRANSPORT = 0;
//        private const byte PACKET_TYPE_APPLICATION = 20;
//        private const byte BULK_DATA_AVAILABLE_PACKET_ID = 2;
//        private const byte START_SESSION_PACKET_ID = 5;
//        private const byte SESSION_STARTED_PACKET_ID = 6;
//        private const float READ_INT_BUFFER_TIMEOUT = 1;   //seconds waiting for packet ariving in buffer
//        private const float READ_BI_BUFFER_TIMEOUT = 1;   //seconds waiting for packet ariving in buffer

//        private const int API_TRUE = 1;
//        private const int API_FALSE = 0;


//        //Misc Constants
//        private const int NOSUPPORT = -1;
//        private const string LOG_LINE = "------------------------------------------------------------------";
//        private const int TIMER_INTERVAL = 10;

//        //Member Variables
//        // member variables
//        // the following are vars not constants as they are populated by the GetCtlCode function
//        private uint IOCTL_GARMIN_USB_API_VERSION = 0;
//        private uint IOCTL_GARMIN_USB_API_INTERRUPT_IN = 0;
//        private uint IOCTL_GARMIN_USB_BULK_OUT_PACKET_SIZE_CTL_CODE = 0;

//        private LinkProtocol miProtocol;								//protocol to be used
//        private int[] mPIDData = new int[27];//PID Data table Data
//        private bool mblnReadBulkIn = false; //gets set when Interrupt in says read bulkin

//        //new buffer class
//        ByteFifo m_bufferINT = new ByteFifo(INTERRUPT_IN_BUFFER_SIZE);
//        ByteFifo m_bufferBI = new ByteFifo(BULK_IN_BUFFER_SIZE);


//        ////create bulk in buffer (base 0)
//        //private byte[] mbytBIBuffer = new byte[BULK_IN_BUFFER_SIZE];
//        //private long mlngBIBufferInPtr = 0; //points at next free buffer byte
//        //private long mlngBIBufferOutPtr = 0; //points at first byte of buffer

//        ////create interrupt in buffer (base 0)
//        //private byte[] mbytINTBuffer = new byte[INTERRUPT_IN_BUFFER_SIZE];
//        //private long mlngINTBufferInPtr = 0; //points at next free buffer byte
//        //private long mlngINTBufferOutPtr = 0;//points at first byte of buffer

//        //threading stuff
//        private Thread InterruptInThread = null;
//        private Thread BulkInThread = null;
//        private ReaderWriterLock objReadWriteLock = new ReaderWriterLock();


//        //		internal enum LinkProtocol
//        //		{
//        //			/// <summary>0</summary>
//        //			L000 = 0,
//        //			/// <summary>1</summary>
//        //			L001 = 1,
//        //			/// <summary>2</summary>
//        //			L002 = 2
//        //		}

//        internal enum usbPacketType //int
//        {
//            usbPacketTypeTransport = 0,
//            usbPacketTypeApplication = 20,
//            usbPacketTypeUnknown = 255,
//        }
//        internal enum usbPacketSource //int
//        {
//            usbPacketSourceUnknown = 0,
//            usbPacketSourceInterruptIn = 1,
//            usbPacketSourceBulkIn = 2,
//            usbPacketSourceBulkOut = 3,
//        }

//        private class clsUsbPacket
//        {
//            internal usbPacketType PacketType = usbPacketType.usbPacketTypeUnknown;
//            internal byte[] Reserved_A = new byte[3];
//            internal short PacketID = 0;
//            internal byte[] Reserved_B = new byte[2];
//            internal int DataSize = 0;
//            internal byte[] Data = null;
//            internal usbPacketSource PacketSource = usbPacketSource.usbPacketSourceUnknown;
//        }
//        /// <summary>
//        /// Thread safe class 
//        /// </summary>
//        private class DeviceHandles
//        {
//            private IntPtr m_hDevice;
//            private IntPtr m_hDevIoEvent;
//            private IntPtr m_hBulkReadEvent;
//            private IntPtr m_hBulkWriteEvent;
//            private bool m_overlapped;

//            private object syncLock = new object();

//            internal IntPtr Device
//            {
//                get
//                {
//                    lock (syncLock)
//                    {
//                        return m_hDevice;
//                    }
//                }
//                set
//                {
//                    lock (syncLock)
//                    {
//                        m_hDevice = value;
//                    }
//                }
//            }
//            internal IntPtr DevIoEvent
//            {
//                get
//                {
//                    lock (syncLock)
//                    {
//                        return m_hDevIoEvent;
//                    }
//                }
//                set
//                {
//                    lock (syncLock)
//                    {
//                        m_hDevIoEvent = value;
//                    }
//                }
//            }
//            internal IntPtr BulkReadEvent
//            {
//                get
//                {
//                    lock (syncLock)
//                    {
//                        return m_hBulkReadEvent;
//                    }
//                }
//                set
//                {
//                    lock (syncLock)
//                    {
//                        m_hBulkReadEvent = value;
//                    }
//                }
//            }
//            internal IntPtr BulkWriteEvent
//            {
//                get
//                {
//                    lock (syncLock)
//                    {
//                        return m_hBulkWriteEvent;
//                    }
//                }
//                set
//                {
//                    lock (syncLock)
//                    {
//                        m_hBulkWriteEvent = value;
//                    }
//                }
//            }
//            internal bool Overlapped
//            {
//                get
//                {
//                    lock (syncLock)
//                    {
//                        return m_overlapped;
//                    }
//                }
//                set
//                {
//                    lock (syncLock)
//                    {
//                        m_overlapped = value;
//                    }
//                }
//            }
//        }

//        //[StructLayout(LayoutKind.Sequential)]
//        //private struct DEVICE_HANDLES
//        //{
//        //    internal IntPtr hDevice;
//        //    internal IntPtr hDevIoEvent;
//        //    internal IntPtr hBulkReadEvent;
//        //    internal IntPtr hBulkWriteEvent;
//        //    internal bool blnOverlapped;
//        //}

//        //private DEVICE_HANDLES mutDeviceHandles;			//stored device handles returned from OpenUSB function
//        private DeviceHandles deviceHandles;			    //stored device handles returned from OpenUSB function

//        internal UsbTransport()
//        {
//            IOCTL_GARMIN_USB_API_VERSION = (uint)GetCtlCode(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS);
//            IOCTL_GARMIN_USB_API_INTERRUPT_IN = (uint)GetCtlCode(FILE_DEVICE_UNKNOWN, 0x850, METHOD_BUFFERED, FILE_ANY_ACCESS);
//            IOCTL_GARMIN_USB_BULK_OUT_PACKET_SIZE_CTL_CODE = (uint)GetCtlCode(FILE_DEVICE_UNKNOWN, 0x851, METHOD_BUFFERED, FILE_ANY_ACCESS);

//            SetPIDValues(LinkProtocol.L001);
//            deviceHandles = new DeviceHandles();

//        }
//        public virtual void Dispose()
//        {
//            Dispose(false);
//        }
//        /// <summary>
//        /// Releases all resources and destroys the object.
//        /// </summary>
//        protected virtual void Dispose(bool disposing)
//        {
//            //Check to see if Dispose has already been called.
//            //and unmanaged resources.
//            if (disposing)
//            {
//                //Dispose managed resources.
//                CloseUsbDevice();
//            }
//            //Release unmanaged resources. If disposing is false,
//            //only the following code is executed.      

//            //Note that this is not thread safe.
//            //Another thread could start disposing the object
//            //after the managed resources are disposed,
//            //but before the disposed flag is set to true.
//        }

//        internal long GetCtlCode(long xi_lngDeviceType, long xi_lngFunction,
//            long xi_lngMethod, long xi_lngAccess)
//        {
//            return (xi_lngDeviceType * Convert.ToInt64((RaiseToPower(2, 16)))) |
//                (xi_lngAccess * Convert.ToInt64((RaiseToPower(2, 14)))) |
//                (xi_lngFunction * Convert.ToInt64((RaiseToPower(2, 2)))) |
//                xi_lngMethod;

//        }

//        internal string GetDeviceName(Guid ID)
//        {

//            IntPtr hDevice = (IntPtr)0;
//            string strDeviceName = "";
//            bool blnSuccess = false;
//            int intBytesReturned = 0;
//            Guid GarminGUID = new Guid(GARMIN_GUID);

//            //strDeviceName = @"\\?\usb#vid_091e&pid_0003#5&31afefbb&0&1#{2c9c45c2-8e7d-4c08-a12d-816bbae722c0}";

//            Waymex.Interop.NativeMethods.DEVICE_INTERFACE_DATA DeviceInterfaceData = new Waymex.Interop.NativeMethods.DEVICE_INTERFACE_DATA();
//            Waymex.Interop.NativeMethods.DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData = new Waymex.Interop.NativeMethods.DEVICE_INTERFACE_DETAIL_DATA(); ;

//            //pass the guid top get a handle to the device
//            //first param indicates the interface required
//            //the last argument indicate we are looking for the interfaces exported by the USB I/O device

//            hDevice = Waymex.Interop.NativeMethods.SetupDiGetClassDevs(ref GarminGUID, null, IntPtr.Zero, Waymex.Interop.NativeMethods.DIGCF_PRESENT | Waymex.Interop.NativeMethods.DIGCF_DEVICEINTERFACE);

//            //check for errors
//            if (hDevice.ToInt32() == -1)
//                throw new USBTransportException(ERR_9005_MSG);

//            //once we have a handle to the device driver we can enumerate through all the devices
//            //that export the particular interface

//            DeviceInterfaceData.cbsize = 28; //Length of data structure in bytes
//            if (Waymex.Interop.NativeMethods.SetupDiEnumDeviceInterfaces(hDevice, IntPtr.Zero, ref GarminGUID, 0, ref DeviceInterfaceData))
//            {

//                // get the rquired buffer size
//                blnSuccess = Waymex.Interop.NativeMethods.SetupDiGetDeviceInterfaceDetail(hDevice, ref DeviceInterfaceData, IntPtr.Zero, 0, ref intBytesReturned, IntPtr.Zero);

//                // sort out the buffer
//                IntPtr Buffer = Marshal.AllocHGlobal(intBytesReturned);

//                DeviceInterfaceDetailData = new Waymex.Interop.NativeMethods.DEVICE_INTERFACE_DETAIL_DATA();
//                DeviceInterfaceDetailData.cbsize = (uint)Marshal.SizeOf(typeof(Waymex.Interop.NativeMethods.DEVICE_INTERFACE_DETAIL_DATA));

//                Marshal.StructureToPtr(DeviceInterfaceDetailData, Buffer, false);

//                //second call to get the actual data
//                if (Waymex.Interop.NativeMethods.SetupDiGetDeviceInterfaceDetail(hDevice, ref DeviceInterfaceData, Buffer, intBytesReturned, ref intBytesReturned, IntPtr.Zero))
//                {
//                    //retrieve the device path from DEVICE_INTERFACE_DETAIL_DATA
//                    // 4 == the offset of first device path character
//                    IntPtr pDevicePath = (IntPtr)((int)Buffer + 4);
//                    strDeviceName = Marshal.PtrToStringAuto(pDevicePath);

//                }
//                else
//                {
//                    //IntPtr pDevicePath = (IntPtr){(int)Buffer + 4);
//                    //strDeviceName = Marshal.PtrToStringAuto(pDevicePath);
//                    throw new USBTransportException(ERR_9006_MSG);
//                }

//                // clean up
//                Marshal.FreeHGlobal(Buffer);

//            }
//            else
//            {
//                Waymex.Interop.NativeMethods.SetupDiDestroyDeviceInfoList(hDevice);
//                throw new USBTransportException(ERR_9006_MSG);
//            }

//            return strDeviceName;
//        }

//        //internal string GetDeviceName(Guid ID)
//        //{

//        //    IntPtr hDevice = (IntPtr)0;
//        //    string strDeviceName = "";
//        //    bool blnSuccess = false;
//        //    int intBytesReturned = 0;
//        //    Guid GarminGUID = new Guid(GARMIN_GUID);

//        //    //strDeviceName = @"\\?\usb#vid_091e&pid_0003#5&31afefbb&0&1#{2c9c45c2-8e7d-4c08-a12d-816bbae722c0}";

//        //    Waymex.Interop.NativeMethods.DEVICE_INTERFACE_DATA deviceInterfaceData = new Waymex.Interop.NativeMethods.DEVICE_INTERFACE_DATA();
//        //    Waymex.Interop.NativeMethods.DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData = new Waymex.Interop.NativeMethods.DEVICE_INTERFACE_DETAIL_DATA();
//        //    //Waymex.Interop.NativeMethods.SP_DEVINFO_DATA deviceInfoData = new Waymex.Interop.NativeMethods.SP_DEVINFO_DATA();
//        //    //Waymex.Interop.NativeMethods.SP_DEVINFO_DATA deviceInfoData = (Waymex.Interop.NativeMethods.SP_DEVINFO_DATA)IntPtr.Zero;
            
//        //    deviceInterfaceData.cbsize = (uint)Marshal.SizeOf(typeof(Waymex.Interop.NativeMethods.DEVICE_INTERFACE_DATA));
//        //    deviceInterfaceDetailData.cbsize = (uint)Marshal.SizeOf(typeof(Waymex.Interop.NativeMethods.DEVICE_INTERFACE_DETAIL_DATA));
//        //    //deviceInfoData.cbsize = (uint)Marshal.SizeOf(typeof(Waymex.Interop.NativeMethods.SP_DEVINFO_DATA));
//        //    //deviceInfoData.ClassGuid = GarminGUID;

//        //    //pass the guid top get a handle to the device
//        //    //first param indicates the interface required
//        //    //the last argument indicate we are looking for the interfaces exported by the USB I/O device
//        //    hDevice = Waymex.Interop.NativeMethods.SetupDiGetClassDevs(ref GarminGUID, null, IntPtr.Zero, Waymex.Interop.NativeMethods.DIGCF_PRESENT | Waymex.Interop.NativeMethods.DIGCF_DEVICEINTERFACE);

//        //    //check for errors
//        //    if (hDevice.ToInt32() == -1)
//        //        throw new USBTransportException(ERR_9005_MSG);

//        //    //once we have a handle to the device driver we can enumerate through all the devices
//        //    //that export the particular interface
//        //    Waymex.Interop.ManagedMethods.SetupDiEnumDeviceInterfaces(hDevice, ref GarminGUID, 0, ref deviceInterfaceData);

//        //    if (Waymex.Interop.NativeMethods.SetupDiEnumDeviceInterfaces(hDevice, IntPtr.Zero, ref GarminGUID, 0, ref deviceInterfaceData))
//        //    {

//        //        // get the rquired buffer size
//        //        blnSuccess = Waymex.Interop.NativeMethods.SetupDiGetDeviceInterfaceDetail(hDevice, ref deviceInterfaceData, ref deviceInterfaceDetailData, 0, ref intBytesReturned, IntPtr.Zero);

//        //        // sort out the buffer
//        //        IntPtr buffer = Marshal.AllocHGlobal(intBytesReturned);
//        //        Marshal.StructureToPtr(deviceInterfaceDetailData, buffer, false);

//        //        //second call to get the actual data
//        //        if (Waymex.Interop.NativeMethods.SetupDiGetDeviceInterfaceDetail(hDevice, ref deviceInterfaceData, ref deviceInterfaceDetailData, intBytesReturned, ref intBytesReturned, IntPtr.Zero))
//        //        {
//        //            //retrieve the device path from DEVICE_INTERFACE_DETAIL_DATA
//        //            // 4 == the offset of first device path character
//        //            IntPtr pDevicePath = (IntPtr)((int)buffer + 4);
//        //            strDeviceName = Marshal.PtrToStringAuto(pDevicePath);

//        //        }
//        //        else
//        //        {
//        //            //IntPtr pDevicePath = (IntPtr){(int)Buffer + 4);
//        //            //strDeviceName = Marshal.PtrToStringAuto(pDevicePath);
//        //            throw new USBTransportException(ERR_9006_MSG);
//        //        }

//        //        // clean up
//        //        Marshal.FreeHGlobal(buffer);

//        //    }
//        //    else
//        //    {
//        //        throw new USBTransportException(ERR_9006_MSG);
//        //        Waymex.Interop.ManagedMethods.SetupDiDestroyDeviceInfoList(hDevice);
//        //    }

//        //    return strDeviceName;
//        //}

//        private void SetPIDValues(LinkProtocol Protocol)
//        {
//            //---------------------------------------------------------------------------
//            //  DESCRIPTION:	This populates the Process ID array (mPIDData). This
//            //                  is used throughout the module to translate the enumerated
//            //                  PID value passed as arguments to the actual value required
//            //                  depending on the protocol selected. This Procedure
//            //                  is called when this class initialises to set default
//            //                  values.
//            //            
//            //	PARAMETERS:
//            //                  Protocol    Enumerated Integer representing L000,
//            //                              L001 or L002.
//            //    
//            //  RETURNS:
//            //                  <None>
//            // 
//            //---------------------------------------------------------------------------
//            //

//            try
//            {
//                //set the common L000/1/2 protocol values
//                mPIDData[0] = 6; //ACK_Byte
//                mPIDData[1] = 21; //NAK_Byte
//                mPIDData[2] = 253; //Protocol_Array
//                mPIDData[3] = 254; //Product_Rqst
//                mPIDData[4] = 255; //Product_Data

//                //set the rest to -1 (NOSUPPORT)
//                for (int f = 5; f <= mPIDData.Length - 1; f++)
//                {
//                    mPIDData[f] = NOSUPPORT;
//                }

//                //determine the additional protocol values to use
//                switch (Protocol)
//                {
//                    //
//                    case LinkProtocol.L001://basic values plus these
//                        {

//                            mPIDData[5] = 10; //CommandData
//                            mPIDData[6] = 12; //Xfer_Cmplt
//                            mPIDData[7] = 14; //Date_Time
//                            mPIDData[8] = 17; //PositionData
//                            mPIDData[9] = 19; //ProximityWaypointData
//                            mPIDData[10] = 27; //Records
//                            mPIDData[11] = 29; //RouteHeader
//                            mPIDData[12] = 30; //Rte_WaypointData
//                            mPIDData[13] = 31; //AlmanacData
//                            mPIDData[14] = 34; //TrackData
//                            mPIDData[15] = 35; //WaypointData
//                            mPIDData[16] = 51; //PvtData
//                            mPIDData[17] = 98; //RouteLinkData
//                            mPIDData[18] = 99; //TrackHeader
//                            mPIDData[19] = 134; //FlghtBook data
//                            mPIDData[20] = 149; //Lap data
//                            mPIDData[21] = 152; //Waypoint Category
//                            mPIDData[22] = 52; //Receiver Measurement
//                            mPIDData[23] = 114; //Satellite data
//                            mPIDData[24] = 48; //Baud Request Data
//                            mPIDData[25] = 49; //Baud Accept Data

//                            break;
//                        }
//                    case LinkProtocol.L002: //basic values plus these
//                        {
//                            mPIDData[5] = 11; //Command_Data
//                            mPIDData[6] = 12; //Xfer_Cmplt
//                            mPIDData[7] = 20; //Date_Time
//                            mPIDData[8] = 24; //Position_Data
//                            mPIDData[9] = NOSUPPORT; //Prx_Wpt_Data (not supported)
//                            mPIDData[10] = 35; //Records
//                            mPIDData[11] = 37; //Rte_Hdr
//                            mPIDData[12] = 39; //Rte_Wpt_Data
//                            mPIDData[13] = 4; //Almanac_Data
//                            mPIDData[15] = 43; //Wpt_Data
//                            mPIDData[16] = NOSUPPORT; //Pvt_Data (not supported)
//                            mPIDData[17] = NOSUPPORT; //Rte_Link_Data (not supported)
//                            mPIDData[18] = NOSUPPORT; //Trk_Hdr (not supported)
//                            mPIDData[19] = NOSUPPORT;
//                            mPIDData[20] = NOSUPPORT;
//                            mPIDData[21] = NOSUPPORT;
//                            mPIDData[22] = NOSUPPORT;
//                            mPIDData[23] = NOSUPPORT;
//                            mPIDData[24] = NOSUPPORT;
//                            mPIDData[25] = NOSUPPORT;

//                            break;
//                        }
//                }
//            }
//            catch
//            {
//                throw;
//            }

//        }

//        private void CloseUsbDevice()
//        {
//            bool blnSuccess = false;
//            int intError = 0;


//            blnSuccess = Waymex.Interop.NativeMethods.CancelIo(deviceHandles.Device);
//            if (!blnSuccess)
//            {
//                intError = Marshal.GetLastWin32Error();
//                //throw new USBTransportException("CloseHandle returned the API Error: " + intError.ToString());
//            }

//            blnSuccess = Waymex.Interop.NativeMethods.CloseHandle(deviceHandles.Device);
//            if (!blnSuccess)
//            {
//                intError = Marshal.GetLastWin32Error();
//                //throw new USBTransportException("CloseHandle returned the API Error: " + intError.ToString());
//            }

//            blnSuccess = Waymex.Interop.NativeMethods.CloseHandle(deviceHandles.DevIoEvent);
//            if (!blnSuccess)
//            {
//                intError = Marshal.GetLastWin32Error();
//                //throw new USBTransportException("CloseHandle returned the API Error: " + intError.ToString());
//            }

//            blnSuccess = Waymex.Interop.NativeMethods.CloseHandle(deviceHandles.BulkReadEvent);
//            if (!blnSuccess)
//            {
//                intError = Marshal.GetLastWin32Error();
//                //throw new USBTransportException("CloseHandle returned the API Error: " + intError.ToString());
//            }

//            blnSuccess = Waymex.Interop.NativeMethods.CloseHandle(deviceHandles.BulkWriteEvent);
//            if (!blnSuccess)
//            {
//                intError = Marshal.GetLastWin32Error();
//                //throw new USBTransportException("CloseHandle returned the API Error: " + intError.ToString());
//            }

//            deviceHandles.Device = IntPtr.Zero;
//            deviceHandles.DevIoEvent = IntPtr.Zero;
//            deviceHandles.BulkReadEvent = IntPtr.Zero;
//            deviceHandles.BulkWriteEvent = IntPtr.Zero;

//        }

//        private void OpenUsbDevice(System.Guid ID, string deviceName)
//        {
//            //DEVICE_HANDLES structDeviceHandles = new DEVICE_HANDLES();
//            string strDeviceName = "";
//            IntPtr lpSecurityAttributes = IntPtr.Zero;
//            IntPtr hDevice = IntPtr.Zero;
//            IntPtr hEventHandleDevIO = IntPtr.Zero;
//            IntPtr hEventHandleRead = IntPtr.Zero;
//            IntPtr hEventHandleWrite = IntPtr.Zero;
//            Waymex.Interop.NativeMethods.SECURITY_ATTRIBUTES utSecurity = new Waymex.Interop.NativeMethods.SECURITY_ATTRIBUTES();
//            int intError = 0;
//            double dblTimer = 0;

//            //initialise the structure for good measure
//            deviceHandles.Device = IntPtr.Zero;
//            deviceHandles.BulkReadEvent = IntPtr.Zero;
//            deviceHandles.BulkWriteEvent = IntPtr.Zero;
//            deviceHandles.DevIoEvent = IntPtr.Zero;

//            //set the initial state to signalled as this means that the overlapped operation has completed
//            lpSecurityAttributes = Marshal.AllocHGlobal(Marshal.SizeOf(utSecurity));

//            Marshal.StructureToPtr(utSecurity, lpSecurityAttributes, true);
//            hEventHandleDevIO = Waymex.Interop.NativeMethods.CreateEvent(lpSecurityAttributes, API_TRUE, API_FALSE, OVERLAPPED_EVENT_NAME_DEVICEIOCTRL);
//            hEventHandleRead = Waymex.Interop.NativeMethods.CreateEvent(lpSecurityAttributes, API_TRUE, API_FALSE, OVERLAPPED_EVENT_NAME_READFILE);
//            hEventHandleWrite = Waymex.Interop.NativeMethods.CreateEvent(lpSecurityAttributes, API_TRUE, API_FALSE, OVERLAPPED_EVENT_NAME_WRITEFILE);
//            Marshal.FreeHGlobal(lpSecurityAttributes);

//            //get the name of the device
//            if (deviceName == null || deviceName == string.Empty)
//                strDeviceName = GetDeviceName(ID);
//            else
//                strDeviceName = deviceName;

//            utSecurity.nLength = 12; //length in bytes of the structure (2 x Int)
//            utSecurity.bInheritHandle = API_TRUE;

//            //check to ensure device is connected
//            if (strDeviceName.Length > 0)
//            {
//                lpSecurityAttributes = Marshal.AllocHGlobal(Marshal.SizeOf(utSecurity));
//                Marshal.StructureToPtr(utSecurity, lpSecurityAttributes, true);

//                //example from Garmin SDK
//                //gHandle = CreateFile(theDevDetailData->DevicePath,GENERIC_READ | GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL );

//                //hDevice = NativeMethods.CreateFile(strDeviceName, FILE_SHARE_WRITE | FILE_SHARE_READ, 0, ptrPointer, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, IntPtr.Zero);
//                dblTimer = Timer();

//                while (true)
//                {
//                    hDevice = Waymex.Interop.NativeMethods.CreateFile(strDeviceName, GENERIC_READ | GENERIC_WRITE, 0, lpSecurityAttributes, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, IntPtr.Zero);
//                    if (hDevice.ToInt32() == INVALID_HANDLE_VALUE)
//                    {
//                        intError = Marshal.GetLastWin32Error();
//                    }
//                    else
//                    {
//                        break;
//                    }

//                    if (Timer() >= dblTimer + OPEN_USB_TIMEOUT)
//                        throw new USBTransportException(ERR_9001_MSG);
//                }

//                deviceHandles.Device = hDevice;
//                deviceHandles.DevIoEvent = hEventHandleDevIO;
//                deviceHandles.BulkReadEvent = hEventHandleRead;
//                deviceHandles.BulkWriteEvent = hEventHandleWrite;
//                deviceHandles.Overlapped = true;

//                Marshal.FreeHGlobal(lpSecurityAttributes);

//            }
//            else
//            {
//                throw new USBTransportException(ERR_9000_MSG);
//            }
//            //return structDeviceHandles;
//        }

//        internal void PortOpen()
//        {
//            PortOpen(string.Empty);
//        }
//        internal void PortOpen(string deviceName)
//        {

//            //close any open ports first
//            //CloseUsbDevice();
//            try
//            {
//                //open the usb device
//                OpenUsbDevice(new Guid(GARMIN_GUID), deviceName);

//                //start the receive threads
//                InterruptInThread = new Thread(new ThreadStart(this.ReadInterruptIn));
//                InterruptInThread.Name = "InterruptIn";
//                InterruptInThread.Priority = ThreadPriority.Normal;
//                InterruptInThread.Start();

//                BulkInThread = new Thread(new ThreadStart(this.ReadBulkIn));
//                BulkInThread.Name = "BulkIn";
//                BulkInThread.Priority = ThreadPriority.Normal;
//                BulkInThread.Start();

//                //reset the bulk in flage in order to read from Interrupt In
//                SwitchToInterruptIn();

//                //start a session
//                StartSession();
//            }
//            catch (Exception e)
//            {
//                throw new USBTransportException(ERR_9000_MSG);
//            }

//        }
//        internal void PortClose()
//        {
//            //stop the receive thread
//            if (InterruptInThread != null)
//                InterruptInThread.Abort();
//            if (BulkInThread != null)
//                BulkInThread.Abort();

//            CloseUsbDevice();

//            //thread wait here to allow things to clear down
//            Thread.Sleep(500);

//        }
//        internal void ClearBuffer()
//        {
//            //clear both the Bulk In and Interrupt In buffers
//            m_bufferBI.Clear();
//            m_bufferINT.Clear();
//            //mlngBIBufferInPtr = mlngBIBufferOutPtr;
//            //mlngINTBufferInPtr = mlngINTBufferOutPtr;
//        }

//        private void StartSession()
//        {
//            try
//            {
//                clsUsbPacket objUsbPacket = new clsUsbPacket();

//                byte[] bytData = new byte[12];
//                byte[] bytTemp = null;
//                byte[] bytUsbPacket = new byte[21]; //21 bytes in the header + the data
//                bool blnSuccess = false;

//                //clear the buffers
//                ClearBuffer();

//                //create the 12 byte session start packet
//                bytData[0] = PACKET_TYPE_TRANSPORT;
//                bytData[1] = 0;
//                bytData[2] = 0;
//                bytData[3] = 0;
//                bytData[4] = START_SESSION_PACKET_ID;
//                bytData[5] = 0;
//                bytData[6] = 0;
//                bytData[7] = 0;
//                bytData[8] = 0;
//                bytData[9] = 0;
//                bytData[10] = 0;
//                bytData[11] = 0;

//                TraceLog.WriteMessage(string.Concat("Transmit Data BO : ", ByteToHex(bytData), " (", ByteToAsc(bytData), ")"), TraceLevel.Verbose, true);

//                //write the packet to Bulk Out
//                objReadWriteLock.AcquireReaderLock(-1);
//                blnSuccess = WriteBulkOut(bytData, deviceHandles);
//                objReadWriteLock.ReleaseReaderLock();

//                //read interupt in until the session started packet arrives or we time out
//                objUsbPacket = GetINTPacket();

//                bytTemp = PacketToByte(objUsbPacket);

//                if (bytTemp.Length > 0)
//                    TraceLog.WriteMessage(string.Concat("Receive Data  INT: ", ByteToHex(bytTemp), " (", ByteToAsc(bytTemp), ")"), TraceLevel.Verbose, true);

//                //if session not started then raise error
//                if (!(objUsbPacket.PacketType == usbPacketType.usbPacketTypeTransport && objUsbPacket.PacketID == SESSION_STARTED_PACKET_ID))
//                {
//                    throw new USBTransportException(ERR_9004_MSG);
//                }
//            }
//            catch (Exception ex)
//            {
//                throw ex;
//            }
//        }
//        internal LinkProtocol Protocol
//        {
//            //---------------------------------------------------------------------------
//            //   DESCRIPTION:    Sets the module level variable to indicate the required
//            //                   link protocol and calls the setPIDValues procedure to
//            //                   set the actual pid values for the protocol selected.
//            //
//            //
//            //   PARAMETERS:     iProt   Enumerated integer indicating the required link
//            //                           level protocol, L000, L001 or L002.
//            //
//            //
//            //   RETURNS:        <none>
//            //
//            //
//            //---------------------------------------------------------------------------

//            set
//            {
//                try
//                {
//                    //save the value for the get property
//                    miProtocol = value;

//                    //use a function to set the actual values so that the same function can
//                    //be called in the initialise event with a default value
//                    SetPIDValues(miProtocol);
//                }
//                catch
//                {
//                    throw;
//                }
//            }
//        }
//        private bool WriteBulkOut(byte[] data, DeviceHandles deviceHandles)
//        {
//            bool success = false;

//            success = Waymex.Interop.ManagedMethods.WriteFileOverlapped(deviceHandles.Device, deviceHandles.BulkWriteEvent, data, BOUT_TIMEOUT);

//            return success;
//        }

//        private clsUsbPacket GetINTPacket()
//        {
//            clsUsbPacket objUsbPacket = new clsUsbPacket();

//            bool blnPktFound = false;
//            bool blnPktStartFound = false;
//            bool blnTimeout = false;
//            byte[] bytPacket = null;
//            byte readByte;
//            byte[] bytTemp = null;
//            //byte[] bytEmptyPacket		= null;
//            int intByte = 0;
//            //byte bytTemp				= 0;
//            System.DateTime dtStart = DateTime.Now;
//            double dblStart = 0;
//            int intDataSize = 0;


//            //Debug.WriteLine("Looking for Packet on IntIn Pipe");

//            try
//            {
//                //create the basic size packet we can always increase it in size later
//                bytPacket = new byte[STANDARD_PACKET_SIZE];

//                //no data in packet array as yet
//                intByte = -1;
//                blnPktFound = false;
//                blnPktStartFound = false;

//                //set the start time
//                dblStart = Timer();

//                //objReadWriteLock.AcquireReaderLock(-1);

//                //run through buffer looking for a packet starting at mlngINTBufferOutPtr
//                while (!(blnPktFound || blnTimeout))
//                {
//                    //only check for packet if buffer is not empty
//                    //objReadWriteLock.AcquireReaderLock(-1);

//                    if (m_bufferINT.Count > 0)
//                    //if (mlngINTBufferOutPtr != mlngINTBufferInPtr)
//                    {

//                        //Debug.WriteLine("Looking for Packet");
//                        System.Windows.Forms.Application.DoEvents();

//                        //have we found the packet start
//                        if (!blnPktStartFound)
//                        {
//                            readByte = m_bufferINT.ReadByte();
//                            if (readByte == PACKET_TYPE_TRANSPORT || readByte == PACKET_TYPE_APPLICATION)
//                                //if (mbytINTBuffer[mlngINTBufferOutPtr] == PACKET_TYPE_TRANSPORT || mbytINTBuffer[mlngINTBufferOutPtr] == PACKET_TYPE_APPLICATION)
//                                blnPktStartFound = true;
//                        }

//                        if (blnPktStartFound)
//                        {
//                            //reset start time as data was found
//                            dblStart = Timer();

//                            //start of packet found so build up packet
//                            intByte = intByte + 1;

//                            bytPacket[intByte] = m_bufferINT.RemoveByte();

//                            //check data size and therefore how many moe bytes to read
//                            if (intByte == 11) //data size integer has been received byte 8-11
//                            {
//                                //get data size
//                                intDataSize = ByteToInt(bytPacket, 8); //byte 8 is where the long value is stored

//                                if (intDataSize > 0)
//                                {
//                                    //increase the size of the packet array accordingly
//                                    bytPacket = ReDimPreserve(bytPacket, STANDARD_PACKET_SIZE + intDataSize);
//                                }
//                            }

//                            //check to see if we have found the packet
//                            if (intByte >= 11 && intByte == intDataSize + 11)
//                            {
//                                blnPktFound = true;
//                                break;
//                            }
//                        }
//                        else
//                        {
//                            //not a USB or Application packet
//                            throw new USBTransportException(ERR_9007_MSG);
//                        }

//                    }	//mlngINTBufferOutPtr != mlngINTBufferInPtr


//                    //check timeout to see if we have been waiting too long (not required on non overlapped operations)
//                    if (Timer() > (dblStart + READ_INT_BUFFER_TIMEOUT))
//                        blnTimeout = true;

//                }

//                //tidy the packet
//                if (blnPktFound)
//                    objUsbPacket = ByteToPacket(bytPacket);
//                else
//                    objUsbPacket.PacketType = usbPacketType.usbPacketTypeUnknown;

//                //set the source
//                objUsbPacket.PacketSource = usbPacketSource.usbPacketSourceInterruptIn;

//                //convert to byte array and log to Trace file if valid usb packet
//                if (objUsbPacket.PacketType != usbPacketType.usbPacketTypeUnknown)
//                {
//                    bytTemp = PacketToByte(objUsbPacket);

//                    if (bytTemp.Length > 0)
//                        TraceLog.WriteMessage(string.Concat("Receive Data  INT: ", ByteToHex(bytTemp), " (", ByteToAsc(bytTemp), ")"), TraceLevel.Verbose, true);
//                }

//            }
//            catch (Exception e)
//            {
//                throw new USBTransportException(e);

//            }
//            finally
//            {
//                if (objReadWriteLock.IsReaderLockHeld)
//                    objReadWriteLock.ReleaseReaderLock();
//                if (objReadWriteLock.IsWriterLockHeld)
//                    objReadWriteLock.ReleaseWriterLock();
//            }


//            return objUsbPacket;

//        }

//        private void ReadInterruptIn()
//        {

//            IntPtr lpBuffer = IntPtr.Zero;

//            try
//            {
//                while (true)
//                {

//                    //check device id
//                    if (deviceHandles.Device == IntPtr.Zero)
//                        throw new USBTransportException(ERR_9003_MSG);

//                    lpBuffer = Marshal.AllocHGlobal((int)GARMIN_USB_INTERRUPT_DATA_SIZE);

//                    byte[] data = Waymex.Interop.ManagedMethods.ReadDeviceIoControlOverlapped(
//                        deviceHandles.Device,
//                        lpBuffer,
//                        GARMIN_USB_INTERRUPT_DATA_SIZE,
//                        deviceHandles.DevIoEvent,
//                        IOCTL_GARMIN_USB_API_INTERRUPT_IN,
//                        IO_TIMEOUT);

//                    if (data != null)
//                    {
//                        m_bufferINT.Add(data);
//                        //TraceLog.WriteMessage(string.Concat("Read IntIn Thread: ", ByteToHex(data), " (", ByteToAsc(data), ")"), TraceLevel.Verbose, true);

//                    }

//                }
//            }
//            catch (ThreadAbortException e)
//            {
//            }
//            finally
//            {
//                Marshal.FreeHGlobal(lpBuffer);
//            }

//        }
//        private static double Timer()
//        {
//            DateTime dtTimer;

//            dtTimer = DateTime.Now;

//            //Debug.Write("Timer() ");     
//            //Debug.WriteLine(dtTimer.Ticks % 711573504 / 1e+007);
//            return (double)dtTimer.Ticks % 711573504 / 1e+007;
//        }

//        private double RaiseToPower(double dblValue, double dblPower)
//        {
//            double dblRetval = dblValue;
//            for (int i = 0; i < dblPower - 1; i++)
//            {
//                dblRetval *= dblValue;
//            }
//            return dblRetval;
//        }

//        private static byte[] ReDimPreserve(byte[] bytArray, int intNewDimension)
//        {
//            //re-dimensions a byte array and preserves the contents
//            if (intNewDimension <= 0 || intNewDimension == 0)
//                throw new ArgumentException(ERR_3010_MSG);

//            byte[] bytNewArray = new byte[intNewDimension];

//            //copy any existing data
//            if (bytArray != null)
//                System.Array.Copy(bytArray, 0, bytNewArray, 0, bytArray.Length);

//            return bytNewArray;
//        }

//        private static string[] ReDimPreserveS(string[] strArray, int intNewDimension)
//        {
//            //re-dimensions a byte array and preserves the contents
//            if (intNewDimension <= 0 || intNewDimension == 0)
//                throw new ArgumentException(ERR_3011_MSG);

//            string[] strNewArray = new string[intNewDimension];

//            //copy any existing data
//            if (strArray != null)
//                System.Array.Copy(strArray, 0, strNewArray, 0, strArray.Length);

//            return strNewArray;
//        }

//        private int ByteToInt(byte[] bytData, int iPosition)
//        {

//            const int DATA_SIZE = 4;

//            int intData = 0;
//            try
//            {
//                //check that the position is OK
//                if (iPosition <= (bytData.GetUpperBound(0) - (DATA_SIZE - 1)))
//                    intData = BitConverter.ToInt32(bytData, iPosition);
//            }
//            catch
//            {
//                throw;
//            }
//            return intData;
//        }

//        private clsUsbPacket ByteToPacket(byte[] bytPacket)
//        {
//            clsUsbPacket usbPacket = new clsUsbPacket();

//            try
//            {
//                if (bytPacket.Length >= 12)
//                {
//                    switch (bytPacket[0])
//                    {
//                        //                  
//                        case 0:
//                            //transport packet
//                            usbPacket.PacketType = usbPacketType.usbPacketTypeTransport;
//                            break;
//                        case 20:
//                            //application packet
//                            usbPacket.PacketType = usbPacketType.usbPacketTypeApplication;
//                            break;
//                        default:
//                            //unknownpacket
//                            usbPacket.PacketType = usbPacketType.usbPacketTypeUnknown;
//                            break;
//                    }

//                    usbPacket.PacketID = (short)ByteToInt(bytPacket, 4);
//                    usbPacket.DataSize = ByteToInt(bytPacket, 8);

//                    //add data array
//                    if (bytPacket.Length > 12)
//                    {
//                        for (int intIndex = 12; intIndex < bytPacket.Length; intIndex++)
//                        {
//                            usbPacket.Data = ReDimPreserve(usbPacket.Data, usbPacket.DataSize);
//                            usbPacket.Data[intIndex - 12] = bytPacket[intIndex];
//                        }
//                    }
//                }
//                else
//                {
//                    //not a valid packet
//                    usbPacket.PacketType = usbPacketType.usbPacketTypeUnknown;
//                }
//            }

//            catch (Exception e)
//            {
//                //need to ignore subscript out of range exceptions and resume next?
//                throw new USBTransportException(e);
//            }

//            return usbPacket;
//        }

//        private GpsPidData USBPacketToPIDData(clsUsbPacket usbPacket)
//        {

//            GpsPidData PData = new GpsPidData();

//            PData.Pid = LookupPID((byte)usbPacket.PacketID);

//            //now the data portion
//            if (usbPacket.DataSize > 0)
//            {
//                for (int f = 0; f < usbPacket.DataSize; f++)
//                {
//                    PData.ByteData = ReDimPreserve(PData.ByteData, usbPacket.DataSize);
//                    if (usbPacket.Data != null)
//                        PData.ByteData[f] = usbPacket.Data[f];
//                }
//            }
//            return PData;
//        }

//        private clsUsbPacket PIDDataToUSBPacket(GpsPidData PData)
//        {
//            clsUsbPacket usbPacket = new clsUsbPacket();
//            int intDataBytes = 0;

//            //get the size of the data
//            if (PData.ByteData != null)
//                intDataBytes = PData.ByteData.Length;
//            else
//                intDataBytes = 0;

//            //need to convert the PData type to a USB packet
//            usbPacket.PacketType = usbPacketType.usbPacketTypeApplication; //all pid data comes from application layer
//            usbPacket.PacketID = (short)mPIDData[(int)PData.Pid];
//            if (intDataBytes > 0)
//                usbPacket.Data = PData.ByteData;
//            usbPacket.DataSize = intDataBytes;

//            return usbPacket;

//        }

//        internal bool TransmitData(GpsPidData PData)
//        {

//            byte[] bytData = null;
//            byte[] bytTemp = null;

//            //create an array for logging purposes
//            bytTemp = new byte[1];

//            bytTemp[0] = (byte)mPIDData[(int)PData.Pid];

//            bytTemp = ByteConcat(bytTemp, PData.ByteData);

//            //create a packet for transmission
//            bytData = PacketToByte(PIDDataToUSBPacket(PData));

//            TraceLog.WriteMessage(string.Concat("Transmit Data BO : ", ByteToHex(bytData), " (", ByteToAsc(bytData), ")"), TraceLevel.Verbose, true);

//            //transmit the data to bulk out
//            objReadWriteLock.AcquireReaderLock(-1);
//            WriteBulkOut(bytData, deviceHandles);
//            objReadWriteLock.ReleaseReaderLock();

//            return true;
//        }

//        private static string ByteToHex(byte[] ByteData)
//        {
//            //---------------------------------------------------------------------------
//            //	DESCRIPTION:	This function converts an array of bytes to a string
//            //					of hex digits representing those bytes.
//            //
//            //
//            //	PARAMETERS:
//            //				ByteData()  Array of bytes.
//            //
//            //	RETURNS:
//            //				String      String of hex character pairs representing
//            //							each byte in the byte array, separated
//            //							with a space.
//            //
//            //---------------------------------------------------------------------------

//            string sTemp = "";
//            string sMessage = "";

//            try
//            {
//                if (ByteData != null)
//                {
//                    for (int f = 0; f <= ByteData.GetUpperBound(0); f++)
//                    {
//                        sTemp = ByteData[f].ToString("X"); //hex returns a string representing the passed number

//                        if (sTemp.Length < 2)
//                        {
//                            sTemp = string.Concat("0", sTemp);
//                        }

//                        sMessage = string.Concat(sMessage, " ", sTemp);
//                    }

//                    return sMessage.Trim();
//                }
//                else
//                {
//                    return "";
//                }
//            }
//            catch
//            {
//                throw;
//            }
//        }

//        private static string ByteToAsc(byte[] ByteData)
//        {
//            //---------------------------------------------------------------------------
//            //   DESCRIPTION:    This function converts an array of bytes to a string
//            //                   of ascii characters representing those bytes.
//            //
//            //
//            //   PARAMETERS:
//            //                   ByteData()  Array of bytes.
//            //
//            //   RETURNS:
//            //                   String      String of ascii character representing
//            //                               each byte in the byte array, an invalid
//            //                               ascii code is represented by a ".".
//            //
//            //---------------------------------------------------------------------------

//            char chrTemp = '.';
//            string sMessage = "";

//            try
//            {
//                if (ByteData != null)
//                {
//                    for (int f = 0; f <= ByteData.GetUpperBound(0); f++)
//                    {
//                        if ((ByteData[f] >= 32) && (ByteData[f] <= 127))
//                        {
//                            chrTemp = Convert.ToChar(ByteData[f]);
//                        }
//                        else
//                        {
//                            chrTemp = '.';
//                        }

//                        sMessage = string.Concat(sMessage, chrTemp.ToString());

//                    }
//                }
//            }
//            catch
//            {
//                throw;
//            }

//            return sMessage.Trim();

//        }

//        internal GpsPidData ReceiveData()
//        {

//            clsUsbPacket usbPacket = new clsUsbPacket();
//            GpsPidData PData = new GpsPidData();


//            try
//            {
//                if (mblnReadBulkIn)
//                {

//                    //read Bulk In try three times before giving up
//                    for (int intCount = 0; intCount < 3; intCount++)
//                    {
//                        usbPacket = GetBIPacket();
//                        if (usbPacket.PacketType != usbPacketType.usbPacketTypeUnknown)
//                            break;
//                    }

//                    if (usbPacket.PacketType == usbPacketType.usbPacketTypeUnknown ||
//                        (usbPacket.PacketType == usbPacketType.usbPacketTypeTransport &&
//                        usbPacket.PacketID == 0))
//                    {
//                        //no longer reading BI
//                        SwitchToInterruptIn();
//                    }

//                }
//                else
//                {

//                    //read Interrupt In try three times
//                    for (int intCount = 0; intCount < 3; intCount++)
//                    {
//                        usbPacket = GetINTPacket();
//                        if (usbPacket.PacketType != usbPacketType.usbPacketTypeUnknown)
//                            break;
//                    }

//                    //see if we should now start reading bulk in
//                    if (usbPacket.PacketType == usbPacketType.usbPacketTypeTransport && usbPacket.PacketID == BULK_DATA_AVAILABLE_PACKET_ID)
//                    {

//                        //no longer reading Int In
//                        SwitchToBulkIn();

//                        //read the first packet from bulk in yry three times
//                        for (int intCount = 0; intCount < 3; intCount++)
//                        {
//                            usbPacket = GetBIPacket();
//                            if (usbPacket.PacketType != usbPacketType.usbPacketTypeUnknown)
//                                break;
//                        }

//                    }
//                }

//                //convert packet to GpsPidData
//                if (usbPacket.PacketType == usbPacketType.usbPacketTypeApplication)
//                {
//                    PData = USBPacketToPIDData(usbPacket);
//                }
//                else
//                {
//                    PData.Pid = ProcessId.Null;
//                    PData.ByteData = null;
//                }
//            }
//            catch
//            {
//                throw;
//            }
//            return PData;

//        }
//        private void SwitchToBulkIn()
//        {
//            //			TraceLog.WriteMessage("Switching to Bulk In", TraceLevel.Verbose, true);
//            objReadWriteLock.AcquireWriterLock(-1);
//            mblnReadBulkIn = true;
//            objReadWriteLock.ReleaseWriterLock();

//            InterruptInThread.Priority = ThreadPriority.BelowNormal;
//            BulkInThread.Priority = ThreadPriority.Normal;

//        }
//        private void SwitchToInterruptIn()
//        {
//            //			TraceLog.WriteMessage("Switching to Interrupt In", TraceLevel.Verbose, true);
//            objReadWriteLock.AcquireWriterLock(-1);
//            mblnReadBulkIn = false;
//            objReadWriteLock.ReleaseWriterLock();

//            BulkInThread.Priority = ThreadPriority.BelowNormal;
//            InterruptInThread.Priority = ThreadPriority.Normal;
//        }

//        private byte[] IntToByte(int lData)
//        {

//            const int DATA_SIZE = 4;

//            byte[] bytData = new byte[DATA_SIZE];

//            try
//            {
//                return BitConverter.GetBytes(lData);
//            }
//            catch
//            {
//                throw;
//            }
//        }
//        private static byte[] ShortToByte(short iData)
//        {
//            return ShortToByte(iData, false);
//        }

//        private static byte[] ShortToByte(short shtData, bool bSingleByte)
//        {

//            byte[] bytData;

//            try
//            {
//                if (bSingleByte)
//                {
//                    bytData = new byte[1];
//                    bytData[0] = (byte)(shtData % 256); //modulus (remainder)
//                }
//                else
//                {
//                    bytData = new byte[2];
//                    bytData[0] = (byte)(shtData % 256);
//                    bytData[1] = (byte)((int)shtData / (int)256); //div (integer division)
//                }
//            }
//            catch
//            {
//                throw;
//            }
//            return bytData;
//        }
//        private byte[] ByteConcat(byte[] array1, byte[] array2)
//        {

//            byte[] data = null;
//            int lenData1 = 0;
//            int lenData2 = 0;

//            try
//            {
//                //determine array lengths
//                if (array1 != null)
//                    lenData1 = array1.Length;
//                if (array2 != null)
//                    lenData2 = array2.Length;

//                //create a new array to hold both incomming arrays
//                data = new byte[lenData1 + lenData2];

//                //copy the arrays
//                if (lenData1 > 0)
//                    Array.Copy(array1, 0, data, 0, lenData1);
//                if (lenData2 > 0)
//                    Array.Copy(array2, 0, data, lenData1, lenData2);

//            }
//            catch
//            {
//                //TraceLog.WriteError(e)
//                throw;
//            }

//            return data;

//        }
//        private byte[] PacketToByte(clsUsbPacket usbPacket)
//        {
//            // byte sizes for usb packet
//            //--------------------------------------
//            // PacketType	= 2 bytes	(integer)
//            // Reserved _A	= 3 bytes	(byte array)
//            // PacketID		= 2 bytes	(integer)
//            // Reserved_B	= 2 bytes	(byte array)
//            // DataSize		= 4 bytes	(integer)
//            // Data			= variable	(byte array)


//            byte[] bytTemp = null;
//            bytTemp = ByteConcat(bytTemp, ShortToByte((short)usbPacket.PacketType, true));
//            bytTemp = ByteConcat(bytTemp, usbPacket.Reserved_A);
//            bytTemp = ByteConcat(bytTemp, ShortToByte(usbPacket.PacketID));
//            bytTemp = ByteConcat(bytTemp, usbPacket.Reserved_B);
//            bytTemp = ByteConcat(bytTemp, IntToByte(usbPacket.DataSize));
//            bytTemp = ByteConcat(bytTemp, usbPacket.Data);

//            return bytTemp;

//        }
//        private void ReadBulkIn()
//        {

//            //const uint BYTES_TO_READ = GARMIN_USB_MAX_BUFFER_SIZE;
//            const uint BYTES_TO_READ = 64;
//            IntPtr lpBuffer = IntPtr.Zero;

//            try
//            {
//                //check device id
//                if (deviceHandles.Device == IntPtr.Zero)
//                    throw new USBTransportException(ERR_9003_MSG);

//                lpBuffer = Marshal.AllocHGlobal((int)BYTES_TO_READ);

//                while (true)
//                {
//                    byte[] data = (Waymex.Interop.ManagedMethods.ReadFileOverlapped(deviceHandles.Device, lpBuffer, BYTES_TO_READ, deviceHandles.BulkReadEvent, IO_TIMEOUT));

//                    if (data != null)
//                    {
//                        m_bufferBI.Add(data);
//                        //TraceLog.WriteMessage(string.Concat("Read BlkIn Thread: ", ByteToHex(data), " (", ByteToAsc(data), ")"), TraceLevel.Verbose, true);
//                    }

//                }
//            }
//            catch (ThreadAbortException e)
//            {
//            }
//            finally
//            {

//                Marshal.FreeHGlobal(lpBuffer);

//                if (objReadWriteLock.IsReaderLockHeld)
//                    objReadWriteLock.ReleaseReaderLock();
//                if (objReadWriteLock.IsWriterLockHeld)
//                    objReadWriteLock.ReleaseWriterLock();

//            }
//        }

//        private ProcessId LookupPID(byte bPid)
//        {
//            //---------------------------------------------------------------------------
//            //   DESCRIPTION:    This function converts a PID to the enumerated
//            //                   equivelent. Internally (i.e. when passing PID's between
//            //                   application layer and link layer PIDs are represented as
//            //                   enumerated integer. This is then converted to the actual
//            //                   PID value in the link layer depending upon the protocol
//            //                   property. This function does the reverse by returning
//            //                   the integer value of the PID based on the actual PID
//            //                   value passed and the protocol curently set.
//            //
//            //   PARAMETERS:
//            //                   bPID        Byte containing actual PID
//            //
//            //   RETURNS:
//            //                   ProcessId   Enumerated integer representing the internal
//            //                               protocol independent PID value. If the
//            //                               PID cannot be found, -1 is returned
//            //
//            //---------------------------------------------------------------------------
//            //
//            //process array should always be populated with valid data

//            int f = 0;
//            //short iPid = 0;

//            ProcessId nResult = ProcessId.Null;

//            try
//            {
//                //run through module level array looking for first occurence of actual Pid
//                for (f = 0; f <= mPIDData.GetUpperBound(0); f++)
//                {
//                    if (mPIDData[f] == (short)bPid)
//                    {
//                        nResult = (ProcessId)f;
//                        break;
//                    }
//                    else
//                    {
//                        nResult = ProcessId.Null;
//                    }
//                }
//            }
//            catch (Exception ex)
//            {
//                throw ex;
//            }
//            return nResult;
//        }

//        private clsUsbPacket GetBIPacket()
//        {

//            bool blnPktFound = false;
//            bool blnPktStartFound = false;
//            bool blnTimeout = false;
//            byte[] bytPacket = null;
//            int intByte = 0;
//            byte[] bytTemp = null;
//            //			datetime dtStart             As Date
//            double dblStart = 0;
//            int intDataSize = 0;
//            clsUsbPacket usbPacket = new clsUsbPacket();
//            byte readByte;

//            //Debug.WriteLine("Looking for Packet on BulkIn Pipe");

//            try
//            {
//                //create the basic size packet we can always increase it in size later
//                bytPacket = new byte[STANDARD_PACKET_SIZE];

//                //no data in packet array as yet
//                intByte = -1;
//                blnPktFound = false;
//                blnPktStartFound = false;

//                //set the start time
//                dblStart = Timer();

//                //run through buffer looking for a packet starting at mlngBIBufferOutPtr
//                while (!(blnPktFound || blnTimeout))
//                {

//                    //only check for packet if buffer is not empty
//                    //objReadWriteLock.AcquireReaderLock(-1);
//                    if (m_bufferBI.Count > 0)
//                    //if (mlngBIBufferOutPtr != mlngBIBufferInPtr)
//                    {

//                        //Debug.WriteLine("Looking for Packet");
//                        System.Windows.Forms.Application.DoEvents();

//                        //have we found the packet start
//                        readByte = m_bufferBI.ReadByte();
//                        if (readByte == PACKET_TYPE_TRANSPORT || readByte == PACKET_TYPE_APPLICATION)
//                            //if (mbytBIBuffer[mlngBIBufferOutPtr] == PACKET_TYPE_TRANSPORT || mbytBIBuffer[mlngBIBufferOutPtr] == PACKET_TYPE_APPLICATION)
//                            blnPktStartFound = true;

//                        if (blnPktStartFound)
//                        {
//                            //reset start time as data was found
//                            dblStart = Timer();

//                            //start of packet found so build up packet
//                            intByte = intByte + 1;

//                            bytPacket[intByte] = m_bufferBI.RemoveByte();

//                            //check data size and therefore how many moe bytes to read
//                            if (intByte == 11) //data size integer has been received byte 8-11
//                            {
//                                //get data size
//                                intDataSize = ByteToInt(bytPacket, 8); //byte 8 is where the long value is stored

//                                if (intDataSize > 0)
//                                {
//                                    //increase the size of the packet array accordingly
//                                    bytPacket = ReDimPreserve(bytPacket, STANDARD_PACKET_SIZE + intDataSize);
//                                }
//                            }

//                            //check to see if we have found the packet
//                            if (intByte >= 11 && intByte == intDataSize + 11)
//                            {
//                                blnPktFound = true;
//                                break;
//                            }
//                        }
//                        else
//                        {
//                            throw new USBTransportException(ERR_9007_MSG);
//                        }//blnPktStartFound

//                    }	//mlngINTBufferOutPtr <> mlngINTBufferInPtr

//                    //check timeout to see if we have been waiting too long (not required on non overlapped operations)
//                    if (Timer() > (dblStart + READ_BI_BUFFER_TIMEOUT))
//                        blnTimeout = true;

//                }


//                //return the packet
//                if (blnPktFound)
//                    usbPacket = ByteToPacket(bytPacket);
//                else
//                    usbPacket.PacketType = usbPacketType.usbPacketTypeUnknown;

//                //set the source
//                usbPacket.PacketSource = usbPacketSource.usbPacketSourceBulkIn;

//                //convert to byte array and log to Trace file if valid usb packet
//                if (usbPacket.PacketType != usbPacketType.usbPacketTypeUnknown)
//                {
//                    bytTemp = PacketToByte(usbPacket);

//                    if (bytTemp.Length > 0)
//                        TraceLog.WriteMessage(string.Concat("Receive Data  BI : ", ByteToHex(bytTemp), " (", ByteToAsc(bytTemp), ")"), TraceLevel.Verbose, true);
//                }


//            }
//            catch (Exception e)
//            {
//                throw new USBTransportException(e);

//            }
//            finally
//            {
//                if (objReadWriteLock.IsReaderLockHeld)
//                    objReadWriteLock.ReleaseReaderLock();
//                if (objReadWriteLock.IsWriterLockHeld)
//                    objReadWriteLock.ReleaseWriterLock();
//            }



//            return usbPacket;

//        }
//    }
//}